Beispiel #1
0
    def test_sortino_ratio_single_asset(self):
        start_date = date(1986, 12, 31)
        end_date = date(2019, 12, 31)

        msft_vals = self.msft_vals.reindex(pd.date_range(start_date, end_date)).ffill()
        risk_free_vals = (
            self.risk_free_vals.dropna()["Close"]
            .reindex(pd.date_range(start_date, end_date))
            .ffill()
        )

        assets = [anda.Asset("MSFT", Decimal(1.0), msft_vals)]

        strategy = anda.Strategy(
            start_date,
            end_date,
            self.starting_balance,
            assets,
            self.contribution_dates,
            self.contribution_amount,
            self.rebalancing_dates,
        )
        self.assertAlmostEqual(
            anda.sortino_ratio(strategy, risk_free_vals), Decimal("1.34"), delta=0.1
        )
def check_overfitting(portfolio, sharpe_threshold=0.5, sortino_threshold=0.5):
    """
    Checks and returns wether strategy is overfitted
    (By comparing to simulation results on rest of available date range)
    Threshold for performance difference considered indicative of overfitting
    """
    proportions = [Decimal(p) for p in portfolio["proportions"]]
    assets_data_all = get_assets(portfolio["tickers"], proportions, None, None)

    s_date = max([asset.values.index[0] for asset in assets_data_all])
    e_date = min(asset.values.index[-1] for asset in assets_data_all)

    new_strat = anda.Strategy(
        s_date,
        e_date,
        portfolio["input_money"],
        assets_data_all,
        validate_dates(s_date, e_date, portfolio["contribution_dates"]),
        portfolio["contribution_amount"],
        validate_dates(s_date, e_date, portfolio["rebalancing_dates"]),
    )

    old_sharpe = (portfolio["sharpe"])[portfolio["name"]]
    old_sortino = (portfolio["sortino"])[portfolio["name"]]

    new_sharpe = round(anda.sharpe_ratio(new_strat, None), 2)
    new_sortino = round(anda.sortino_ratio(new_strat, None), 2)

    return (old_sharpe > new_sharpe + Decimal(sharpe_threshold)) or (
        old_sortino > new_sortino + Decimal(sortino_threshold))
Beispiel #3
0
    def test_dividends_multiple_assets(self):
        start_date = date(1986, 12, 31)
        end_date = date(2019, 12, 31)

        msft_vals = self.msft_vals.reindex(pd.date_range(start_date, end_date)).ffill()
        msft_dividends = self.msft_dividends.reindex(
            pd.date_range(start_date, end_date)
        ).dropna()
        aapl_vals = self.aapl_vals.reindex(pd.date_range(start_date, end_date)).ffill()
        aapl_dividends = self.aapl_dividends.reindex(
            pd.date_range(start_date, end_date)
        ).dropna()

        assets = [
            anda.Asset("MSFT", Decimal("0.5"), msft_vals, msft_dividends),
            anda.Asset("AAPL", Decimal("0.5"), aapl_vals, aapl_dividends),
        ]

        strategy = anda.Strategy(
            start_date,
            end_date,
            self.starting_balance,
            assets,
            self.contribution_dates,
            self.contribution_amount,
            self.rebalancing_dates,
        )
        self.assertAlmostEqual(
            anda.total_return(strategy)[end_date], Decimal("9856511.60"), delta=1
        )
Beispiel #4
0
    def test_msft(self):
        start_date = date(1986, 3, 13)
        end_date = date(2019, 1, 1)
        msft_vals = self.msft_vals.reindex(pd.date_range(start_date, end_date)).ffill()
        msft_dividends = self.msft_dividends.reindex(
            pd.date_range(start_date, end_date)
        ).dropna()

        assets = [anda.Asset("MSFT", Decimal("1.0"), msft_vals, msft_dividends)]

        strategy = anda.Strategy(
            start_date,
            end_date,
            self.starting_balance,
            assets,
            self.contribution_dates,
            self.contribution_amount,
            self.rebalancing_dates,
        )
        annual_returns = anda.relative_yearly_returns(strategy)
        expected = [
            #   (year, change)
            (1987, Decimal("125")),
            (1988, Decimal("-2")),
            (2000, Decimal("-63")),
            (2001, Decimal("53")),
            (2002, Decimal("-22")),
            (2003, Decimal("7")),
            (2017, Decimal("41")),
            (2018, Decimal("21")),
        ]
        for year, change in expected:
            self.assertAlmostEqual(
                annual_returns.at[date(year, 1, 1)], change, delta=Decimal("0.5")
            )
Beispiel #5
0
    def test_short_time(self):
        start_date = date(1989, 1, 4)
        end_date = date(1989, 12, 31)

        self.msft_vals = self.msft_vals.reindex(
            pd.date_range(start_date, end_date)
        ).ffill()

        assets = [
            anda.Asset("MSFT", Decimal("1.0"), self.msft_vals),
        ]

        strategy = anda.Strategy(
            start_date,
            end_date,
            self.starting_balance,
            assets,
            self.contribution_dates,
            self.contribution_amount,
            self.rebalancing_dates,
        )

        self.assertRaises(anda.InsufficientTimeframe, lambda: anda.best_year(strategy))
        self.assertRaises(anda.InsufficientTimeframe, lambda: anda.worst_year(strategy))
        self.assertRaises(
            anda.InsufficientTimeframe, lambda: anda.best_year_no(strategy)
        )
        self.assertRaises(
            anda.InsufficientTimeframe, lambda: anda.worst_year_no(strategy)
        )
Beispiel #6
0
    def test_sortino_ratio_multi_asset(self):
        start_date = date(1989, 12, 29)
        end_date = date(2000, 12, 29)

        msft_vals = self.msft_vals.reindex(pd.date_range(start_date, end_date)).ffill()
        berkshire_vals = self.berkshire_vals.reindex(
            pd.date_range(start_date, end_date)
        ).ffill()
        risk_free_vals = (
            self.risk_free_vals.dropna()["Close"]
            .reindex(pd.date_range(start_date, end_date))
            .ffill()
        )

        assets = [
            anda.Asset("MSFT", Decimal(0.6), msft_vals),
            anda.Asset("BRK-A", Decimal(0.4), berkshire_vals),
        ]

        strategy = anda.Strategy(
            start_date,
            end_date,
            self.starting_balance,
            assets,
            self.contribution_dates,
            self.contribution_amount,
            self.rebalancing_dates,
        )
        self.assertAlmostEqual(
            anda.sortino_ratio(strategy, risk_free_vals), Decimal("1.56"), delta=0.2
        )
Beispiel #7
0
    def test_simple(self):
        start_date = date(1989, 1, 4)
        end_date = date(2010, 1, 1)

        self.msft_vals = self.msft_vals.reindex(
            pd.date_range(start_date, end_date)
        ).ffill()

        assets = [
            anda.Asset("MSFT", Decimal("1.0"), self.msft_vals),
        ]

        strategy = anda.Strategy(
            start_date,
            end_date,
            self.starting_balance,
            assets,
            self.contribution_dates,
            self.contribution_amount,
            self.rebalancing_dates,
        )

        b = anda.best_year(strategy)
        w = anda.worst_year(strategy)

        self.assertAlmostEqual(b, Decimal("120"), delta=2)
        self.assertAlmostEqual(w, Decimal("-63"), delta=2)
        self.assertAlmostEqual(b, Decimal("122"), delta=Decimal("0.5"))
        self.assertAlmostEqual(w, Decimal("-63"), delta=Decimal("0.5"))

        b_year = anda.best_year_no(strategy)
        w_year = anda.worst_year_no(strategy)

        self.assertEqual(b_year, 1991)
        self.assertEqual(w_year, 2000)
Beispiel #8
0
    def test_mult_assets(self):
        starting_balance = Decimal("100.00")
        contribution_dates = set()
        contribution_amount = Decimal("0.0")
        rebalancing_dates = set()

        assets = [
            anda.Asset("GOLD", Decimal("0.4"), self.gold_data),
            anda.Asset("SLV", Decimal("0.6"), self.silver_data),
        ]

        strategy = anda.Strategy(
            self.start,
            self.end,
            starting_balance,
            assets,
            contribution_dates,
            contribution_amount,
            rebalancing_dates,
        )

        roi = anda.total_return(strategy)
        self.assertEqual(roi[self.start], Decimal("100.00"))
        self.assertEqual(roi[self.start + timedelta(days=14)], Decimal("220.40"))
        self.assertEqual(roi[self.end], Decimal("320.40"))
def get_strategy(
    tickers,
    proportions,
    handles,
    start_date,
    end_date,
    input_money,
    contribution_amount,
    contribution_dates,
    rebalancing_dates,
):
    """
    Initializes and returns strategy object
    """
    weights = [p for p in proportions if p is not None]
    normalise(weights)

    # Separate user-supplied data from Thalia-known data.
    user_assets = []
    thalia_tickers = []
    thalia_weights = []
    for ticker, weight, handle in zip(tickers, weights, handles):
        if handle is None:
            thalia_tickers.append(ticker)
            thalia_weights.append(weight)
        else:
            user_assets.append((ticker, weight, handle))

    thalia_data = get_assets(thalia_tickers, thalia_weights, start_date,
                             end_date)

    if thalia_data is None:
        # raise error
        return None

    user_supplied_data = [
        anda.Asset(ticker, weight, user_csv.retrieve(handle))
        for ticker, weight, handle in user_assets
    ]
    all_asset_data = user_supplied_data + thalia_data

    real_start_date = max(asset.values.index[0] for asset in all_asset_data)
    real_end_date = min(asset.values.index[-1] for asset in all_asset_data)

    if real_end_date < real_start_date:
        # raise error
        return None

    strategy = anda.Strategy(
        real_start_date,
        real_end_date,
        input_money,
        all_asset_data,
        contribution_dates,
        contribution_amount,
        rebalancing_dates,
    )
    return strategy
def test_check_overfitting(mock_finda):
    # make sure overfitting threshold is something reasonable
    Config.OVERFITTING_THRESH = 0.7
    # create strategy
    start_date = pd.Timestamp(2000, 3, 13)
    end_date = pd.Timestamp(2000, 3, 15)
    # Will trigger for any positive threshold (T = -1.7)
    asset_data = callbacks.get_assets(["OVF"], [1.0], start_date, end_date)
    strategy = anda.Strategy(
        start_date,
        end_date,
        1,
        asset_data,
        [],
        0,
        [],
    )
    assert callbacks.check_overfitting(strategy)
    # Will not trigger for any reasonable threshold
    asset_data = callbacks.get_assets(["NOVF"], [1.0], start_date, end_date)
    strategy = anda.Strategy(
        start_date,
        end_date,
        1,
        asset_data,
        [],
        0,
        [],
    )
    assert not callbacks.check_overfitting(strategy)
    # All values, should never trigger since performance should be the same
    start_date = pd.Timestamp(2000, 3, 9)
    end_date = pd.Timestamp(2000, 3, 18)
    asset_data = callbacks.get_assets(["NOVF"], [1.0], start_date, end_date)
    strategy = anda.Strategy(
        start_date,
        end_date,
        1,
        asset_data,
        [],
        0,
        [],
    )
    assert not callbacks.check_overfitting(strategy)
Beispiel #11
0
    def test_max_drawdown_single_asset(self):
        start_date = date(1986, 12, 31)
        end_date = date(2019, 12, 31)

        msft_vals = self.msft_vals.reindex(pd.date_range(start_date, end_date)).ffill()

        assets = [anda.Asset("MSFT", Decimal("1.0"), msft_vals)]

        strategy = anda.Strategy(
            start_date,
            end_date,
            self.starting_balance,
            assets,
            self.contribution_dates,
            self.contribution_amount,
            self.rebalancing_dates,
        )
        self.assertAlmostEqual(anda.max_drawdown(strategy), Decimal("72.33"), delta=2.5)
Beispiel #12
0
    def test_single_asset(self):
        starting_balance = Decimal("23.46")
        contribution_dates = set()
        contribution_amount = None
        rebalancing_dates = set()

        assets = [anda.Asset("Gold", Decimal("1.0"), self.gold_data)]

        strategy = anda.Strategy(
            self.start,
            self.end,
            starting_balance,
            assets,
            contribution_dates,
            contribution_amount,
            rebalancing_dates,
        )

        roi = anda.total_return(strategy)
        self.assertEqual(roi.at[self.start], Decimal("23.46"))
        self.assertEqual(roi.at[date(2000, 1, 12)], Decimal("24.75"))
        self.assertEqual(roi.at[self.end], Decimal("25.69"))
Beispiel #13
0
    def test_no_money(self):
        starting_balance = Decimal("0.00")
        contribution_dates = pd.date_range(
            self.start, self.end, freq=timedelta(days=4)
        )[1:]
        contribution_amount = Decimal("1000.00")
        rebalancing_dates = set()

        assets = [anda.Asset("ST", Decimal("1.0"), self.rock_data)]

        strategy = anda.Strategy(
            self.start,
            self.end,
            starting_balance,
            assets,
            contribution_dates,
            contribution_amount,
            rebalancing_dates,
        )

        roi = anda.total_return(strategy)
        roi  # Just the lack of exception *should* be a sign of success.
Beispiel #14
0
    def test_contribution(self):
        starting_balance = Decimal("1.00")
        contribution_dates = self.dates
        contribution_amount = Decimal("1.00")
        rebalancing_dates = set()

        assets = [anda.Asset("ST", Decimal("1.00"), self.rock_data)]

        strategy = anda.Strategy(
            self.start,
            self.end,
            starting_balance,
            assets,
            contribution_dates,
            contribution_amount,
            rebalancing_dates,
        )

        roi = anda.total_return(strategy)
        self.assertEqual(Decimal("2.00"), roi.at[self.start])
        for (day, next_day) in zip(self.dates, self.dates[1:]):
            self.assertEqual(roi[day] + Decimal("1.00"), roi[next_day])
Beispiel #15
0
    def test_dividends_single_asset(self):
        start_date = date(1986, 12, 31)
        end_date = date(2019, 12, 31)

        msft_vals = self.msft_vals.reindex(pd.date_range(start_date, end_date)).ffill()
        msft_dividends = self.msft_dividends.reindex(
            pd.date_range(start_date, end_date)
        ).dropna()

        assets = [anda.Asset("MSFT", Decimal("1.0"), msft_vals, msft_dividends)]

        strategy = anda.Strategy(
            start_date,
            end_date,
            self.starting_balance,
            assets,
            self.contribution_dates,
            self.contribution_amount,
            self.rebalancing_dates,
        )
        self.assertAlmostEqual(
            anda.total_return(strategy)[end_date], Decimal("14599199.22"), delta=1
        )
Beispiel #16
0
    def test_max_drawdown_multi_asset(self):
        start_date = date(1989, 12, 29)
        end_date = date(2000, 12, 29)

        msft_vals = self.msft_vals.reindex(pd.date_range(start_date, end_date)).ffill()
        berkshire_vals = self.berkshire_vals.reindex(
            pd.date_range(start_date, end_date)
        ).ffill()

        assets = [
            anda.Asset("MSFT", Decimal(0.6), msft_vals),
            anda.Asset("BRK-A", Decimal(0.4), berkshire_vals),
        ]

        strategy = anda.Strategy(
            start_date,
            end_date,
            self.starting_balance,
            assets,
            self.contribution_dates,
            self.contribution_amount,
            self.rebalancing_dates,
        )
        self.assertAlmostEqual(anda.max_drawdown(strategy), Decimal("59.03"), delta=3)
Beispiel #17
0
    def test_rebalancing(self):
        # TODO
        starting_balance = Decimal("10000.00")
        contribution_dates = set()
        contribution_amount = Decimal("0.0")
        rebalancing_dates = self.dates

        assets = [
            anda.Asset("GOLD", Decimal("0.5"), self.gold_data),
            anda.Asset("SLV", Decimal("0.5"), self.silver_data),
        ]

        strategy = anda.Strategy(
            self.start,
            self.end,
            starting_balance,
            assets,
            contribution_dates,
            contribution_amount,
            rebalancing_dates,
        )

        roi = anda.total_return(strategy)
        roi  # gotta keep flake8 happy.