From c7d80187f2a860d0cd368d7c8644cc556c5035a0 Mon Sep 17 00:00:00 2001 From: wassname Date: Sat, 15 Jul 2017 10:58:10 +0800 Subject: [PATCH] improving shift ans scale --- src/data/utils.py | 3 +-- src/environments/portfolio.py | 14 +++++++++----- test/test_env.py | 31 ++++++++++++++++++++++++++----- test/test_utils.py | 6 +++--- 4 files changed, 39 insertions(+), 15 deletions(-) diff --git a/src/data/utils.py b/src/data/utils.py index 6837728..65cd4a4 100644 --- a/src/data/utils.py +++ b/src/data/utils.py @@ -7,8 +7,7 @@ def random_shift(x, fraction): """Apply a random shift to a pandas series.""" min_x, max_x = np.min(x), np.max(x) m = np.random.uniform(-fraction, fraction, size=x.shape) + 1 - c = np.random.uniform(-fraction, fraction, size=x.shape) * x.std() - return np.clip(x * m + c, min_x, max_x) + return np.clip(x * m, min_x, max_x) def normalize(x): diff --git a/src/environments/portfolio.py b/src/environments/portfolio.py index fbc5180..1eda7ab 100644 --- a/src/environments/portfolio.py +++ b/src/environments/portfolio.py @@ -24,22 +24,24 @@ class DataSrc(object): self.augument = augument self.scale = scale + df = df.copy() # add return/y1 as last col pairs = df.columns.levels[0] for pair in pairs: x = df[pair].close - df[pair, "return"] = (x + eps*2) / (x.shift() + eps) + df[pair, "return"] = (x + eps) / (x.shift() + eps) df = df[1:] # data processing if scale: - # df = (df - df.mean(0) + eps) / (df.max(0) - df.min(0) + eps) - df = df.apply(lambda x: normalize(x)) + # don't normalize return + df = df.apply( + lambda x: normalize(x) if x.name[1] != 'return' else x) # get rid of NaN's - df = df.fillna(method="pad") df.replace(np.nan, 0, inplace=True) + df = df.fillna(method="pad") self._data = df.copy() self.asset_names = self._data.columns.levels[0].tolist() @@ -64,8 +66,10 @@ class DataSrc(object): data = self._data[self.idx:self.idx + self.steps].copy() # scale each run to the begining of the episode so they look the same + # but not return if self.scale: - data = data.apply(lambda x: scale_to_start(x)) + data = data.apply( + lambda x: scale_to_start(x) if x.name[1] != 'return' else x) # augument data to prevent overfitting data = data.apply(lambda x: random_shift(x, self.augument)) diff --git a/test/test_env.py b/test/test_env.py index c75da07..be94f83 100644 --- a/test/test_env.py +++ b/test/test_env.py @@ -6,19 +6,29 @@ from src.environments.portfolio import PortfolioEnv def test_portfolio_env(): df = pd.read_hdf('./data/poliniex_30m.hf', key='train') asset_names = df.columns.levels[0] - # action - w = np.random.random((len(asset_names))) - w /= w.sum() + np.random.seed(0) env = PortfolioEnv(df=df) - env.reset() - obs, reward, done, info = env.step(w) + + obs = env.reset() + for _ in range(20): + + w = np.random.random((len(asset_names))) + w /= w.sum() + + obs, reward, done, info = env.step(w) + assert not done + + df_info = pd.DataFrame(info) + final_value = df_info.portfolio_value.iloc[-1] + assert final_value > 0.75, 'should retain most value with 20 random steps' def test_portfolio_env_hold(): df = pd.read_hdf('./data/poliniex_30m.hf', key='train') asset_names = df.columns.levels[0] + np.random.seed(0) env = PortfolioEnv(df=df) env.reset() for _ in range(5): @@ -27,3 +37,14 @@ def test_portfolio_env_hold(): df = pd.DataFrame(info) assert df.portfolio_value.iloc[-1] > 0.9999, 'portfolio should retain value if holding bitcoin' + + +def test_return_not_scaled(): + df = pd.read_hdf('./data/poliniex_30m.hf', key='train') + np.random.seed(0) + env1 = PortfolioEnv(df=df, scale=True) + np.random.seed(0) + env0 = PortfolioEnv(df=df, scale=False) + a = env0.src._data.xs('return', axis=1, level='Price').tail(5) + b = env1.src._data.xs('return', axis=1, level='Price').tail(5) + assert (a == b).all().all(), 'returns should not be scaled' diff --git a/test/test_utils.py b/test/test_utils.py index e2ddca2..b68a7b0 100644 --- a/test/test_utils.py +++ b/test/test_utils.py @@ -10,9 +10,9 @@ def test_random_shift(): assert (s == s1).all(), 'should not do anything if given 0' s2 = random_shift(s.copy(), 0.05) - assert (s2 / s).max() > 1.0, 'should shift more than 0.00 given 0.05' - assert (s2 / s).max() < 1.1, 'should shift less than 0.10 given 0.05' - np.testing.assert_almost_equal((s2 / s).mean(), 1.00, 2) + shift = (s2 / s) + np.testing.assert_almost_equal(shift.mean(), 1.00, 2) + np.testing.assert_almost_equal(shift.max(), 1.05, 2) def test_normalize():