Beispiel #1
0
    def recalc_queryset(self, **kwargs):
        n_days = kwargs.get("n_days", 30)
        stocks_to_consider = filter_stocks_to_search(
            self.request, kwargs.get("what_to_search"))
        period1 = kwargs.get("period1", 20)
        period2 = kwargs.get("period2", 200)

        matching_stocks = set()
        self.timeframe = Timeframe(past_n_days=n_days)

        assert period2 > period1
        df = company_prices(stocks_to_consider,
                            Timeframe(past_n_days=n_days + period2),
                            transpose=False)
        # print(df)
        wanted_dates = set(self.timeframe.all_dates())
        for s in filter(lambda asx_code: asx_code in df.columns,
                        stocks_to_consider):
            last_price = df[s]
            # we filter now because it is after the warm-up period for MA200....
            ma20 = last_price.rolling(period1).mean().filter(
                items=wanted_dates, axis=0)
            ma200 = (last_price.rolling(period2,
                                        min_periods=min([
                                            50, 3 * period1
                                        ])).mean().filter(items=wanted_dates,
                                                          axis=0))

            matching_dates = set(
                [xo[1] for xo in calc_ma_crossover_points(ma20, ma200)])
            if len(matching_dates.intersection(wanted_dates)) > 0:
                matching_stocks.add(s)
        return list(matching_stocks)
Beispiel #2
0
def get_dataset(dataset_wanted, request, timeframe=None):
    assert (dataset_wanted in set(["market_sentiment", "eps-per-sector"])
            or dataset_wanted.startswith("kmeans-")
            or dataset_wanted.startswith("financial-metrics-")
            or dataset_wanted.startswith("stock-quotes-"))

    if timeframe is None:
        timeframe = Timeframe(past_n_days=300)

    if dataset_wanted == "market_sentiment":
        df = cached_all_stocks_cip(timeframe)
        return df
    elif dataset_wanted == "kmeans-watchlist":
        _, _, _, _, df = make_kmeans_cluster_dataframe(
            timeframe, 7, user_watchlist(request.user))
        return df
    elif dataset_wanted == "kmeans-etfs":
        _, _, _, _, df = make_kmeans_cluster_dataframe(timeframe, 7,
                                                       all_etfs())
        return df
    elif dataset_wanted.startswith("stock-quotes-"):
        stock = dataset_wanted[len("stock-quotes-"):]
        validate_stock(stock)
        df = company_prices([stock],
                            timeframe=timeframe,
                            fields=all_stock_fundamental_fields,
                            missing_cb=None)
        df['stock_code'] = stock
        return df
    elif dataset_wanted.startswith("kmeans-sector-"):
        sector_id = int(dataset_wanted[14:])
        sector = Sector.objects.get(sector_id=sector_id)
        if sector is None or sector.sector_name is None:
            raise Http404("No stocks associated with sector")
        asx_codes = all_sector_stocks(sector.sector_name)
        _, _, _, _, df = make_kmeans_cluster_dataframe(timeframe, 7, asx_codes)
        return df
    elif dataset_wanted.startswith("financial-metrics-"):
        stock = dataset_wanted[len("financial-metrics-"):]
        validate_stock(stock)
        df = financial_metrics(stock)
        if df is not None:
            # excel doesnt support timezones, so we remove it first
            colnames = [d.strftime("%Y-%m-%d") for d in df.columns]
            df.columns = colnames
            # FALLTHRU
        return df
    elif dataset_wanted == "eps-per-sector":
        df, _ = pe_trends_df(Timeframe(past_n_days=180))
        df = make_pe_trends_eps_df(df, stocks_by_sector())
        df = df.set_index("asx_code", drop=True)
        return df
    else:
        raise ValueError("Unsupported dataset {}".format(dataset_wanted))
Beispiel #3
0
def test_timeframe(data, expected):
    #print(data)
    tf = Timeframe(**data, today=datetime(year=2021, month=3, day=31).date()) # note today is fixed date for easier testing
    all_dates = tf.all_dates()
    assert len(all_dates) == expected[0]
    assert tf.n_days == expected[0]
    assert tf.n_days == len(tf)
    assert str(tf) == f"Timeframe: {data}"
    assert tf.description == expected[1]
    all_dates_expected = expected[2]
    if all_dates_expected is not None:
        assert all_dates == all_dates_expected
        assert all_dates_expected[-1] in tf
        assert not '1999-01-01' in tf
    validate_date(tf.most_recent_date)
Beispiel #4
0
def test_company_prices(quotation_fixture, monkeypatch):
    #expected_dates = ['2021-01-01', '2021-01-02', '2021-01-03', '2021-01-04', '2021-01-05', '2021-01-06']
    monkeypatch.setattr(mdl, 'make_superdf', mock_superdf_all_stocks)

    # basic check
    required_timeframe = Timeframe(from_date='2021-01-01', n=6)
    df = company_prices(['ABC', 'OTHER'], 
                        required_timeframe,
                        fields='last_price', 
                        missing_cb=None, 
                        transpose=True)
    assert isinstance(df, pd.DataFrame)
   
    assert len(df) == 2
    assert list(df.index) == ['ABC', 'OTHER']
    assert list(df.columns) == ['2021-01-01', '2021-01-02', '2021-01-03', '2021-01-04', '2021-01-05', '2021-01-06']
    is_other_nan = list(np.isnan(df.loc['OTHER']))
    assert is_other_nan == [False, True, True, True, True, True]
   
    # check impute missing functionality
    df2 = company_prices(['ABC', 'OTHER'], required_timeframe, fields='last_price', transpose=True)
    assert list(df2.loc['OTHER']) == [0.0, 0.0, 0.0, 0.0, 0.0, 0.0]

    # finally check that a multi-field DataFrame is as requested
    monkeypatch.setattr(mdl, 'make_superdf', mock_superdf_many_fields)
Beispiel #5
0
def rank_cumulative_change(df: pd.DataFrame, timeframe: Timeframe):
    cum_sum = defaultdict(float)
    #print(df)
    for date in filter(lambda k: k in df.columns, timeframe.all_dates()):
        for code, price_change in df[date].fillna(0.0).iteritems():
            cum_sum[code] += price_change
        rank = pd.Series(cum_sum).rank(method='first', ascending=False)
        df[date] = rank

    all_available_dates = df.columns
    avgs = df.mean(axis=1)  # NB: do this BEFORE adding columns...
    assert len(avgs) == len(df)
    df['x'] = all_available_dates[-1]
    df['y'] = df[all_available_dates[-1]]

    bins = ['top', 'bin2', 'bin3', 'bin4', 'bin5', 'bottom']
    average_rank_binned = pd.cut(avgs, len(bins), bins)
    assert len(average_rank_binned) == len(df)
    df['bin'] = average_rank_binned
    df['asx_code'] = df.index
    stock_sector_df = stocks_by_sector(
    )  # make one DB call (cached) rather than lots of round-trips
    #print(stock_sector_df)
    stock_sector_df = stock_sector_df.set_index('asx_code')
    #print(df.index)
    df['sector'] = [stock_sector_df.loc[code].sector_name for code in df.index]
    df = pd.melt(df,
                 id_vars=['asx_code', 'bin', 'sector', 'x', 'y'],
                 var_name='date',
                 value_name='rank',
                 value_vars=all_available_dates)
    df['date'] = pd.to_datetime(df['date'], format="%Y-%m-%d")
    df['x'] = pd.to_datetime(df['x'], format="%Y-%m-%d")
    return df
Beispiel #6
0
    def get_queryset(self, **kwargs):
        # user never run this view before?
        if kwargs == {}:
            print("WARNING: no form parameters specified - returning empty queryset")
            return Quotation.objects.none()

        self.sector = kwargs.get("sector", self.sector)
        self.sector_id = int(Sector.objects.get(sector_name=self.sector).sector_id)
        wanted_stocks = all_sector_stocks(self.sector)
        print("Found {} stocks matching sector={}".format(len(wanted_stocks), self.sector))
        mrd = latest_quotation_date('ANZ')
        report_top_n = kwargs.get('report_top_n', None)
        report_bottom_n = kwargs.get('report_bottom_n', None)
        if report_top_n is not None or report_bottom_n is not None:
            cip_sum = selected_cached_stocks_cip(wanted_stocks, Timeframe(past_n_days=90)).transpose().sum().to_frame(name="percent_cip")
            #print(cip_sum)
            top_N = set(cip_sum.nlargest(report_top_n, "percent_cip").index) if report_top_n is not None else set()
            bottom_N = set(cip_sum.nsmallest(report_bottom_n, "percent_cip").index) if report_bottom_n is not None else set()
            wanted_stocks = top_N.union(bottom_N)
        print("Requesting valid quotes for {} stocks".format(len(wanted_stocks)))
        self.qs = valid_quotes_only(mrd).filter(asx_code__in=wanted_stocks)
        if len(self.qs) < len(wanted_stocks):
            got = set([q.asx_code for q in self.qs.all()])
            missing_stocks = wanted_stocks.difference(got)
            warning(self.request, f"could not obtain quotes for all stocks as at {mrd}: {missing_stocks}")
        return self.qs
Beispiel #7
0
    def form_valid(self, form):
        exclude = form.cleaned_data["excluded_stocks"]
        n_days = form.cleaned_data["n_days"]
        algo = form.cleaned_data["method"]
        portfolio_cost = form.cleaned_data["portfolio_cost"]
        exclude_price = form.cleaned_data.get("exclude_price", None)
        max_stocks = form.cleaned_data.get("max_stocks", 80)
        stocks = self.stocks()

        if exclude is not None:
            if isinstance(exclude, str):
                exclude = exclude.split(",")
            stocks = set(stocks).difference(exclude)

        if form.cleaned_data["exclude_etfs"]:
            stocks = set(stocks).difference(all_etfs())
            print(f"After excluding ETFs: {len(stocks)} stocks remain")

        self.timeframe = Timeframe(past_n_days=n_days)
        self.results = optimise_portfolio(
            stocks,
            self.timeframe,
            algo=algo,
            max_stocks=max_stocks,
            total_portfolio_value=portfolio_cost,
            exclude_price=exclude_price,
            warning_cb=lambda msg: warning(self.request, msg),
            returns_by=form.cleaned_data.get("returns_by", None),
        )
        return render(self.request, self.template_name,
                      self.get_context_data())
Beispiel #8
0
def show_trends(request):
    user = request.user
    validate_user(user)
    stocks = user_watchlist(user)
    timeframe = Timeframe(past_n_days=300)
    ld = LazyDictionary()
    ld["cip_df"] = lambda ld: selected_cached_stocks_cip(stocks, timeframe)
    ld["trends"] = lambda ld: calculate_trends(ld["cip_df"])
    ld["rank"] = lambda ld: rank_cumulative_change(
        ld["cip_df"].filter(ld["trends"].keys(), axis="index"), timeframe
    )

    trending_companies_plot = cache_plot(
        f"{user.username}-watchlist-trends",
        lambda ld: plot_company_rank(ld),
        datasets=ld,
    )

    context = {
        "watchlist_trends": ld["trends"],
        "timeframe": timeframe,
        "trending_companies_uri": trending_companies_plot,
        "trending_companies_plot_title": "Trending watchlist stocks (ranked): {}".format(
            timeframe.description
        ),
    }
    return render(request, "watchlist-rank.html", context=context)
Beispiel #9
0
class DividendYieldSearch(
        SearchMixin,
        LoginRequiredMixin,
        MultipleObjectTemplateResponseMixin,
        FormView,
):
    form_class = DividendSearchForm
    template_name = "search_form.html"  # generic template, not specific to this view
    action_url = "/search/by-yield"
    ordering = ("-annual_dividend_yield",)
    timeframe = Timeframe(past_n_days=30)
    qs = None

    def additional_context(self, context):
        """
            Return the additional fields to be added to the context by render_to_response(). Subclasses
            should override this rather than the template design pattern implementation of render_to_response()
        """
        assert context is not None
        return {
            "title": "Find by dividend yield or P/E",
            "sentiment_heatmap_title": "Matching stock heatmap: {}".format(self.timeframe.description),
            "n_top_bottom": 20
        }

    def render_to_response(self, context, **kwargs):
        """
        Invoke show_companies()
        """
        assert kwargs is not None
        context.update(self.additional_context(context))
        
        return show_companies( # will typically invoke show_companies() to share code across all views
            self.qs,
            self.request,
            self.timeframe,
            context,
            template_name=self.template_name
        )

    def get_queryset(self, **kwargs):
        if kwargs == {}:
            return Quotation.objects.none()

        as_at = latest_quotation_date('ANZ')
        min_yield = kwargs.get("min_yield") if "min_yield" in kwargs else 0.0
        max_yield = kwargs.get("max_yield") if "max_yield" in kwargs else 10000.0
        results = Quotation.objects.filter(fetch_date=as_at).\
                                    filter(annual_dividend_yield__gte=min_yield).\
                                    filter(annual_dividend_yield__lte=max_yield)

        if "min_pe" in kwargs:
            results = results.filter(pe__gte=kwargs.get("min_pe"))
        if "max_pe" in kwargs:
            results = results.filter(pe__lt=kwargs.get("max_pe"))
        if "min_eps_aud" in kwargs:
            results = results.filter(eps__gte=kwargs.get("min_eps_aud"))
        self.qs = results
        return self.qs
Beispiel #10
0
def show_total_earnings(request):
    validate_user(request.user)

    def data_factory(df: pd.DataFrame) -> pd.DataFrame:
        df = df.pivot(
            index=["asx_code", "fetch_date"], columns="field_name", values="field_value"
        )
        required = (df.number_of_shares > 0) & (df.eps > 0.0)
        df = df[required]  # ignore stocks which have unknowns
        # print(df)
        df["total_earnings"] = df["eps"] * df["number_of_shares"]
        df = df.dropna(how="any", axis=0)
        df = df.reset_index()
        df = df.pivot(index="asx_code", columns="fetch_date", values="total_earnings")
        df = df.merge(stocks_by_sector(), left_index=True, right_on="asx_code")
        df = df.set_index("asx_code", drop=True)
        df = df.groupby("sector_name").sum()
        df["sector_name"] = df.index
        df = df.melt(id_vars="sector_name", var_name="fetch_date")
        assert set(df.columns) == set(["sector_name", "fetch_date", "value"])
        df["fetch_date"] = pd.to_datetime(df["fetch_date"], format="%Y-%m-%d")

        return df

    def plot(df: pd.DataFrame) -> p9.ggplot:
        plot = (
            p9.ggplot(
                df,
                p9.aes(
                    x="fetch_date",
                    y="value",
                    color="sector_name",  # group="sector_name"
                ),
            )
            + p9.geom_line(size=1.2)
            + p9.facet_wrap("~sector_name", ncol=2, scales="free_y")
            + p9.scale_y_continuous(labels=label_shorten)
        )
        return user_theme(
            plot,
            y_axis_label="Total sector earnings ($AUD, positive contributions only)",
            figure_size=(12, 14),
            subplots_adjust={"wspace": 0.25},
        )

    ld = LazyDictionary()
    ld["timeframe"] = Timeframe(past_n_days=180)
    ld["pe_trends_df"] = lambda ld: pe_trends_df(ld["timeframe"])
    ld["df"] = lambda ld: data_factory(ld["pe_trends_df"])
    context = {
        "title": "Earnings per sector over time",
        "timeframe": ld["timeframe"],
        "plot_uri": cache_plot(
            f"total-earnings-by-sector:{ld['timeframe'].description}",
            lambda ld: plot(ld["df"]),
            datasets=ld,
        ),
    }
    return render(request, "total_earnings_by_sector.html", context=context)
Beispiel #11
0
def get_dataset(dataset_wanted):
    assert dataset_wanted in set(["market_sentiment"])

    if dataset_wanted == "market_sentiment":
        df = cached_all_stocks_cip(Timeframe())
        return df
    else:
        raise ValueError("Unsupported dataset {}".format(dataset_wanted))
Beispiel #12
0
    def recalc_queryset(self, **kwargs):
        if kwargs == {} or not any(
            ["name" in kwargs, "activity" in kwargs, "sector" in kwargs]):
            return Quotation.objects.none()

        wanted_name = kwargs.get("name", "")
        wanted_activity = kwargs.get("activity", "")
        if len(wanted_name) > 0 or len(wanted_activity) > 0:
            matching_companies = find_named_companies(wanted_name,
                                                      wanted_activity)
        else:
            matching_companies = all_stocks()
        sector = kwargs.get("sector", self.DEFAULT_SECTOR)
        sector_id = int(Sector.objects.get(sector_name=sector).sector_id)
        sector_stocks = all_sector_stocks(sector)
        if kwargs.get("sector_enabled", False):
            matching_companies = matching_companies.intersection(sector_stocks)
        print("Found {} companies matching: name={} or activity={}".format(
            len(matching_companies), wanted_name, wanted_activity))

        report_top_n = kwargs.get("report_top_n", None)
        report_bottom_n = kwargs.get("report_bottom_n", None)
        self.timeframe = Timeframe(past_n_days=90)
        ld = LazyDictionary()
        ld["sector"] = sector
        ld["sector_id"] = sector_id
        ld["sector_companies"] = sector_stocks
        if len(matching_companies) > 0:
            ld["cip_df"] = selected_cached_stocks_cip(matching_companies,
                                                      self.timeframe)
        else:
            ld["cip_df"] = pd.DataFrame()
        ld["sector_performance_df"] = lambda ld: make_sector_performance_dataframe(
            ld["cip_df"], ld["sector_companies"])
        ld["sector_performance_plot"] = lambda ld: self.sector_performance(ld)
        self.ld = ld
        wanted_stocks = self.filter_top_bottom(ld, matching_companies,
                                               report_top_n, report_bottom_n)

        print("Requesting valid quotes for {} stocks".format(
            len(wanted_stocks)))
        quotations_as_at, actual_mrd = valid_quotes_only(
            "latest", ensure_date_has_data=True)
        ret = quotations_as_at.filter(asx_code__in=wanted_stocks)
        if len(ret) < len(wanted_stocks):
            got = set([q.asx_code
                       for q in self.qs.all()]) if self.qs else set()
            missing_stocks = wanted_stocks.difference(got)
            warning(
                self.request,
                f"could not obtain quotes for all stocks as at {actual_mrd}: {missing_stocks}",
            )

        print("Showing results for {} companies".format(
            len(matching_companies)))
        ret, _ = latest_quote(tuple(matching_companies))
        return ret
Beispiel #13
0
 def dataframe(ld: LazyDictionary) -> pd.DataFrame:
     momentum_timeframe = Timeframe(
         past_n_days=n_days + 200
     )  # to warmup MA200 function
     df = company_prices(
         (stock,),
         momentum_timeframe,
         fields=all_stock_fundamental_fields,
         missing_cb=None,
     )
     return df
Beispiel #14
0
def make_portfolio_performance_dataframe(
        stocks: Iterable[str], timeframe: Timeframe,
        purchases: Iterable[VirtualPurchase]) -> pd.DataFrame:
    def sum_portfolio(df: pd.DataFrame, date_str: str, stock_items):
        validate_date(date_str)

        portfolio_worth = sum(
            map(lambda t: df.at[t[0], date_str] * t[1], stock_items))
        return portfolio_worth

    df = company_prices(stocks, timeframe, transpose=True)
    rows = []
    stock_count = defaultdict(int)
    stock_cost = defaultdict(float)
    portfolio_cost = 0.0

    for d in [
            datetime.strptime(x, "%Y-%m-%d").date()
            for x in timeframe.all_dates()
    ]:
        d_str = str(d)
        if d_str not in df.columns:  # not a trading day?
            continue
        purchases_to_date = filter(lambda vp, d=d: vp.buy_date <= d, purchases)
        for purchase in purchases_to_date:
            if purchase.buy_date == d:
                portfolio_cost += purchase.amount
                stock_count[purchase.asx_code] += purchase.n
                stock_cost[purchase.asx_code] += purchase.amount

        portfolio_worth = sum_portfolio(df, d_str, stock_count.items())
        # print(df)
        # emit rows for each stock and aggregate portfolio
        for asx_code in stocks:
            cur_price = df.at[asx_code, d_str]
            if np.isnan(cur_price):  # price missing? ok, skip record
                continue
            assert cur_price is not None and cur_price >= 0.0
            stock_worth = cur_price * stock_count[asx_code]

            rows.append({
                "portfolio_cost": portfolio_cost,
                "portfolio_worth": portfolio_worth,
                "portfolio_profit": portfolio_worth - portfolio_cost,
                "stock_cost": stock_cost[asx_code],
                "stock_worth": stock_worth,
                "stock_profit": stock_worth - stock_cost[asx_code],
                "date": d_str,
                "stock": asx_code,
            })

    df = pd.DataFrame.from_records(rows)
    df["date"] = pd.to_datetime(df["date"], format="%Y-%m-%d")
    return df
Beispiel #15
0
def market_sentiment(request, n_days=21, n_top_bottom=20, sector_n_days=180):
    validate_user(request.user)
    assert n_days > 0
    assert n_top_bottom > 0
    timeframe = Timeframe(past_n_days=n_days)
    sector_timeframe = Timeframe(past_n_days=sector_n_days)
    df = cached_all_stocks_cip(timeframe)
    sector_df = cached_all_stocks_cip(sector_timeframe)
    sentiment_plot, top10, bottom10 = plot_heatmap(df,
                                                   timeframe,
                                                   n_top_bottom=n_top_bottom)
    sector_performance_plot = plot_market_wide_sector_performance(sector_df)

    context = {
        "sentiment_data":
        sentiment_plot,
        "n_days":
        timeframe.n_days,
        "n_stocks_plotted":
        len(df),
        "n_top_bottom":
        n_top_bottom,
        "best_ten":
        top10,
        "worst_ten":
        bottom10,
        "watched":
        user_watchlist(request.user),
        "sector_performance":
        sector_performance_plot,
        "sector_performance_title":
        "Cumulative sector avg. performance: {}".format(
            sector_timeframe.description),
        "title":
        "Market sentiment: {}".format(timeframe.description),
        "market_cap_distribution_plot":
        plot_market_cap_distribution(tuple(df.index),
                                     latest_quotation_date('ANZ'),
                                     sector_df.columns[0])
    }
    return render(request, "market_sentiment_view.html", context=context)
Beispiel #16
0
def show_all_stocks(request):
    all_dates = all_available_dates()
    if len(all_dates) < 1:
        raise Http404("No ASX price data available!")
    ymd = all_dates[-1]
    validate_date(ymd)
    qs = valid_quotes_only(ymd)
    timeframe = Timeframe()
    return show_companies(qs, request, timeframe, extra_context={
        "title": "All stocks",
        "sentiment_heatmap_title": "All stock sentiment: {}".format(timeframe.description)
    })
Beispiel #17
0
def show_watched(request):
    validate_user(request.user)
    matching_companies = user_watchlist(request.user)

    timeframe = Timeframe()
    return show_companies(
        matching_companies, request, timeframe, {
            "title":
            "Stocks you are watching",
            "sentiment_heatmap_title":
            "Watchlist stocks sentiment: {}".format(timeframe.description),
        })
Beispiel #18
0
 def process_form(self, cleaned_data):
     timeframe = Timeframe(past_n_days=cleaned_data.get("timeframe", 180))
     bond = cleaned_data.get("bond_name", None)
     df = get_bond_prices(bond, timeframe)
     plot_uri = cache_plot(
         f"{bond}-{timeframe.description}",
         lambda ld: self.make_plot(df, timeframe),
     )
     return {
         "title": "Visualise bond yields by country",
         "plot_uri": "/png/" + plot_uri,
         "plot_title": f"Bond prices: {bond} over {timeframe.description}",
     }
Beispiel #19
0
def show_increasing_yield_stocks(request):
    validate_user(request.user)
    matching_companies = increasing_yield(None)
    extra_context = {
        "title": "Stocks with increasing yield over past 300 days",
        "sentiment_heatmap_title": "Sentiment for selected stocks",
    }
    return show_companies(
        matching_companies,
        request,
        Timeframe(),
        extra_context,
    )
Beispiel #20
0
def show_etfs(request):
    validate_user(request.user)
    matching_codes = all_etfs()
    extra_context = {
        "title": "Exchange Traded funds over past 300 days",
        "sentiment_heatmap_title": "Sentiment for ETFs",
    }
    return show_companies(
        matching_codes,
        request,
        Timeframe(),
        extra_context,
    )
Beispiel #21
0
class MomentumSearch(DividendYieldSearch):
    """
    Search for momentum related signals by finding cross over points between 20-day moving average and 200 day moving average.
    We try to provide a warm-up period of data (depending on what it is in the database) so that the user-requested period has good data.
    """

    form_class = MomentumSearchForm
    action_url = "/search/momentum-change"
    template_name = "search_form.html"

    def additional_context(self, context):
        ret = super().additional_context(context)
        ret.update({
            "title": "Momentum Search",
            "sentiment_heatmap_title": "Momentum stock sentiment",
        })
        return ret

    def recalc_queryset(self, **kwargs):
        n_days = kwargs.get("n_days", 30)
        stocks_to_consider = filter_stocks_to_search(
            self.request, kwargs.get("what_to_search"))
        period1 = kwargs.get("period1", 20)
        period2 = kwargs.get("period2", 200)

        matching_stocks = set()
        self.timeframe = Timeframe(past_n_days=n_days)

        assert period2 > period1
        df = company_prices(stocks_to_consider,
                            Timeframe(past_n_days=n_days + period2),
                            transpose=False)
        # print(df)
        wanted_dates = set(self.timeframe.all_dates())
        for s in filter(lambda asx_code: asx_code in df.columns,
                        stocks_to_consider):
            last_price = df[s]
            # we filter now because it is after the warm-up period for MA200....
            ma20 = last_price.rolling(period1).mean().filter(
                items=wanted_dates, axis=0)
            ma200 = (last_price.rolling(period2,
                                        min_periods=min([
                                            50, 3 * period1
                                        ])).mean().filter(items=wanted_dates,
                                                          axis=0))

            matching_dates = set(
                [xo[1] for xo in calc_ma_crossover_points(ma20, ma200)])
            if len(matching_dates.intersection(wanted_dates)) > 0:
                matching_stocks.add(s)
        return list(matching_stocks)
Beispiel #22
0
def show_watched(request):
    validate_user(request.user)
    matching_companies = user_watchlist(request.user)

    timeframe = Timeframe()
    return show_companies(
        matching_companies,
        request,
        timeframe,
        {
            "title": "Stocks you are watching",
            "sentiment_heatmap_title": "Watchlist sentiment heatmap",
        },
    )
Beispiel #23
0
 def get_queryset(self, **kwargs):
     if any([kwargs == {}, "threshold" not in kwargs, "timeframe_in_days" not in kwargs]):
         return Quotation.objects.none()
     threshold_percentage = kwargs.get("threshold")
     self.timeframe = Timeframe(past_n_days=kwargs.get("timeframe_in_days", 30))
     df = find_movers(
         threshold_percentage,
         self.timeframe,
         kwargs.get("show_increasing", False),
         kwargs.get("show_decreasing", False),
         kwargs.get("max_price", None)
     )
     self.qs, _ = latest_quote(tuple(df.index))
     return self.qs
Beispiel #24
0
    def process_form(self, cleaned_data: dict) -> dict:
        timeframe = Timeframe(past_n_days=cleaned_data["timeframe"])
        crypto_symbol = cleaned_data.get("currency", "BTC")
        crypto_prices = get_crypto_prices(crypto_symbol, timeframe)
        # print(crypto_prices)
        plot_uri = cache_plot(
            f"{crypto_symbol}-{timeframe.description}",
            lambda ld: self.make_plot(crypto_prices, timeframe),
        )

        return {
            "title": "Visualize cryptocurrency prices over time",
            "plot_uri": "/png/" + plot_uri,
            "plot_title": f"{crypto_symbol} over {timeframe.description}",
        }
Beispiel #25
0
 def process_form(self, cleaned_data):
     timeframe = Timeframe(past_n_days=cleaned_data.get("timeframe", 180))
     commodity_str = cleaned_data.get("commodity", "Gold")
     df = get_commodity_prices(commodity_str, timeframe)
     plot_uri = cache_plot(
         f"{commodity_str}-{timeframe.description}",
         lambda ld: self.make_plot(df, timeframe),
     )
     return {
         "title":
         "Visualize commodity prices over time",
         "plot_uri":
         "/png/" + plot_uri,
         "plot_title":
         f"Commodity prices: {commodity_str} over {timeframe.description}",
     }
Beispiel #26
0
 def render_to_response(self, context):
     context.update({
         "title": "Find companies by financial metric",
         "sentiment_heatmap_title": "Matching stock sentiment",
     })
     warning(
         self.request,
         "Due to experimental data ingest, results may be wrong/inaccurate/misleading. Use at own risk",
     )
     return show_companies(
         self.object_list,  # ie. return result from self.get_queryset()
         self.request,
         Timeframe(past_n_days=30),
         context,
         self.template_name,
     )
Beispiel #27
0
def show_outliers(request, stocks, n_days=30, extra_context=None):
    assert stocks is not None
    assert n_days is not None  # typically integer, but desired_dates() is polymorphic
    timeframe = Timeframe(past_n_days=n_days)
    cip = selected_cached_stocks_cip(stocks, timeframe)
    outliers = detect_outliers(stocks, cip)
    extra_context = {
        "title": "Unusual stock behaviours: {}".format(timeframe.description),
        "sentiment_heatmap_title": "Outlier stocks: sentiment",
    }
    return show_companies(
        outliers,
        request,
        timeframe,
        extra_context,
    )
Beispiel #28
0
 def form_valid(self, form):
     sector = form.cleaned_data.get('sector', "Communication Services")
     norm_method = form.cleaned_data.get('normalisation_method', None)
     n_days = form.cleaned_data.get('n_days', 30)
     stocks = all_sector_stocks(sector)
     timeframe = Timeframe(past_n_days=n_days)
     cip = selected_cached_stocks_cip(stocks, timeframe)
     context = self.get_context_data()
     boxplot, winner_results = plot_boxplot_series(cip, normalisation_method=norm_method)
     context.update({
         'title': "Past {} day sector performance: box plot trends".format(n_days),
         'n_days': n_days,
         'sector': sector,
         'plot': boxplot,
         'winning_stocks': winner_results
     })
     return render(self.request, self.template_name, context)
Beispiel #29
0
    def form_valid(self, form):
        sector = form.cleaned_data.get("sector", "Communication Services")
        norm_method = form.cleaned_data.get("normalisation_method", None)
        n_days = form.cleaned_data.get("n_days", 30)
        ld = LazyDictionary()
        ld["stocks"] = lambda ld: all_sector_stocks(sector)
        ld["timeframe"] = Timeframe(past_n_days=n_days)
        ld["cip_df"] = lambda ld: selected_cached_stocks_cip(
            ld["stocks"], ld["timeframe"])

        context = self.get_context_data()

        def winner_results(df: pd.DataFrame) -> list:
            # compute star performers: those who are above the mean on a given day counted over all days
            count = defaultdict(int)
            avg = df.mean(axis=0)
            for col in df.columns:
                winners = df[df[col] > avg[col]][col]
                for winner in winners.index:
                    count[winner] += 1
            results = []
            for asx_code, n_wins in count.items():
                x = df.loc[asx_code].sum()
                # avoid "dead cat bounce" stocks which fall spectacularly and then post major increases in percentage terms
                if x > 0.0:
                    results.append((asx_code, n_wins, x))
            return list(reversed(sorted(results, key=lambda t: t[2])))

        context.update({
            "title":
            "Past {} day sector performance: box plot trends".format(n_days),
            "n_days":
            n_days,
            "sector":
            sector,
            "plot_uri":
            cache_plot(
                f"{sector}-recent-sector-view-{ld['timeframe'].description}-{norm_method}",
                lambda ld: plot_boxplot_series(
                    ld["cip_df"], normalisation_method=norm_method),
                datasets=ld,
            ),
            "winning_stocks":
            winner_results(ld["cip_df"]),
        })
        return render(self.request, self.template_name, context)
Beispiel #30
0
def show_stock_sector(request, stock):
    validate_stock(stock)
    validate_user(request.user)

    _, company_details = stock_info(stock, lambda msg: warning(request, msg))
    sector = company_details.sector_name if company_details else None
    all_stocks_cip = cached_all_stocks_cip(Timeframe(past_n_days=180))

    # invoke separate function to cache the calls when we can
    c_vs_s_plot, sector_momentum_plot, sector_companies = analyse_sector_performance(
        stock, sector, all_stocks_cip)
    point_score_plot = net_rule_contributors_plot = None
    if sector_companies is not None:
        point_score_plot, net_rule_contributors_plot = \
                plot_point_scores(stock,
                                  sector_companies,
                                  all_stocks_cip,
                                  default_point_score_rules())

    context = {
        "is_sector":
        True,
        "asx_code":
        stock,
        "sector_momentum_plot":
        sector_momentum_plot,
        "sector_momentum_title":
        "{} sector stocks".format(sector),
        "company_versus_sector_plot":
        c_vs_s_plot,
        "company_versus_sector_title":
        "{} vs. {} performance".format(stock, sector),
        "point_score_plot":
        point_score_plot,
        "point_score_plot_title":
        "Points score due to price movements",
        "net_contributors_plot":
        net_rule_contributors_plot,
        "net_contributors_plot_title":
        "Contributions to point score by rule",
    }
    return render(request, "stock_sector.html", context)