From 6250988e2c96ce46c7bcd79f816411f691db9ded Mon Sep 17 00:00:00 2001 From: Axel Gard Date: Fri, 14 Jun 2024 14:53:49 +0200 Subject: [PATCH] made strat work with float allocation and fixed bugs in back test --- cira/strategy.py | 42 ++++++++++++++++++++++++++++++++++++------ tests/test_strategy.py | 41 +++++++++++++++++++++++++++++------------ 2 files changed, 65 insertions(+), 18 deletions(-) diff --git a/cira/strategy.py b/cira/strategy.py index 53e9807..da3dac2 100644 --- a/cira/strategy.py +++ b/cira/strategy.py @@ -46,12 +46,19 @@ def load(cls, file_path): class Randomness(Strategy): - def __init__(self, lower: int = -1, upper: int = 1, seed=0) -> None: + def __init__( + self, + lower: float | int = -1, + upper: float | int = 1, + seed=0, + use_float: bool = False, + ) -> None: super().__init__(name="Randomness") random.seed(seed) self.a = lower self.b = upper self.allocation = [] + self.use_float = use_float def iterate( self, @@ -61,8 +68,31 @@ def iterate( cash=float, ) -> np.ndarray: al = np.array( - [random.randint(self.a, self.b) for _ in range(len(prices.keys()))] + [ + random.uniform(float(self.a), float(self.b)) + for _ in range(len(prices.keys())) + ] ) + if not self.use_float: + al = al.astype(int) + self.allocation.append(al) + return al + + +class DollarCostAveraging(Strategy): + def __init__(self, amount: int | float = 1) -> None: + super().__init__(name="DollarCostAveraging") + self.amount = amount + self.allocation = [] + + def iterate( + self, + feature_data: pd.DataFrame, + prices: pd.DataFrame, + portfolio: np.ndarray, + cash=float, + ) -> np.ndarray: + al = np.array([self.amount for _ in range(len(prices.keys()))]) self.allocation.append(al) return al @@ -117,11 +147,11 @@ def back_test( } assert len(feature_data) == len(asset_prices) total_value = capital - nr_of_asset = np.zeros([len(asset_prices.keys())], int) + nr_of_asset = np.zeros([len(asset_prices.keys())], float) i = 0 for t, cur_price in asset_prices.iterrows(): - if len(asset_prices) == i + 1: - break + # if len(asset_prices) == i + 1: + # break if total_value > 0: f_data = feature_data.iloc[: i + 1] p_data = asset_prices.iloc[: i + 1] @@ -138,7 +168,7 @@ def back_test( np.matmul(cur_price.values.T, allocation) + use_fees * fees(cur_price.values, allocation) ) # - capital) - if asking < capital: + if asking <= capital: capital -= asking nr_of_asset += allocation total_value = np.matmul(cur_price.values.T, nr_of_asset) + capital diff --git a/tests/test_strategy.py b/tests/test_strategy.py index e79e8a9..32c04b4 100644 --- a/tests/test_strategy.py +++ b/tests/test_strategy.py @@ -1,11 +1,12 @@ import cira from . import util import os +import numpy as np def test_iterate(): feature_data = util.stock_data - strat = cira.strategy.Randomness(seed=2**12) + strat = cira.strategy.DollarCostAveraging(amount=1) prices = feature_data["close"].to_frame() change_in_portfolio = strat.iterate(feature_data, prices.iloc[-1], 10_000) assert change_in_portfolio.tolist() == [1] @@ -26,20 +27,36 @@ def test_storing_strategy(): def test_backtest(): feature_data = util.stock_data - strat = cira.strategy.Randomness(seed=2**14) + strat = cira.strategy.DollarCostAveraging(amount=1) prices = feature_data["close"].to_frame() - prices["close"] = [10, 100, 10_000, 100, 10] + prices["close"] = [10, 10, 5, 20, 10] - resutlt = cira.strategy.back_test_against_buy_and_hold(strat, feature_data, prices, 10_000) - resutlt = resutlt.dropna() + resutlt = cira.strategy.back_test(strat, feature_data, prices, 20, use_fees=False) - res = resutlt[strat.name].values.tolist() - res = [int(r) for r in res] + res = resutlt[strat.name].values.astype(int).tolist() + assert res == [20, 20, 10, 40, 20] - assert res == [9999, 10089, 20029, 20029] - s = cira.strategy.ByAndHold() - res = resutlt[s.name].values.tolist() - res = [int(r) for r in res] +def test_backtest_float(): + feature_data = util.stock_data + strat = cira.strategy.DollarCostAveraging(amount=0.5) + prices = feature_data["close"].to_frame() + prices["close"] = [10, 10, 5, 20, 10] + + resutlt = cira.strategy.back_test(strat, feature_data, prices, 10, use_fees=False) + + res = resutlt[strat.name].values.astype(int).tolist() + assert res == [10, 10, 5, 20, 10] + + +def test_backtest_fees(): + feature_data = util.stock_data + strat = cira.strategy.DollarCostAveraging(amount=1) + prices = feature_data["close"].to_frame() + prices["close"] = [10, 10, 10, 10, 10] + + cira.strategy.FEE_RATE = 0.1 + resutlt = cira.strategy.back_test(strat, feature_data, prices, 100, use_fees=True) - assert res == [9961, 96361, 9600361, 96361] + res = resutlt[strat.name].values.astype(int).tolist() + assert res == [99, 98, 97, 96, 95] \ No newline at end of file