-
Notifications
You must be signed in to change notification settings - Fork 100
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #576 from ashis2004/h12
Stress testing financial portfolio added
- Loading branch information
Showing
3 changed files
with
650 additions
and
0 deletions.
There are no files selected for viewing
265 changes: 265 additions & 0 deletions
265
...main/Stress testing financial portfolio/Stress_Testing_Financial_Portfolios_with_RL.ipynb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,265 @@ | ||
{ | ||
"cells": [ | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 1, | ||
"metadata": { | ||
"colab": { | ||
"base_uri": "https://localhost:8080/" | ||
}, | ||
"id": "l4r3fazkwMCa", | ||
"outputId": "4bb294cb-24ba-43a6-edcb-c5ff6b7deff4" | ||
}, | ||
"outputs": [ | ||
{ | ||
"output_type": "stream", | ||
"name": "stdout", | ||
"text": [ | ||
"Dataset saved to financial_portfolio_data.csv\n" | ||
] | ||
} | ||
], | ||
"source": [ | ||
"import pandas as pd\n", | ||
"import numpy as np\n", | ||
"import datetime\n", | ||
"\n", | ||
"# Parameters for the synthetic data generation\n", | ||
"num_records = 1000\n", | ||
"start_date = datetime.datetime(2020, 1, 1)\n", | ||
"date_range = pd.date_range(start_date, periods=num_records, freq='H')\n", | ||
"\n", | ||
"# Generate synthetic data\n", | ||
"np.random.seed(42) # For reproducibility\n", | ||
"asset_1_prices = np.cumsum(np.random.randn(num_records)) + 100\n", | ||
"asset_2_prices = np.cumsum(np.random.randn(num_records)) + 150\n", | ||
"asset_3_prices = np.cumsum(np.random.randn(num_records)) + 200\n", | ||
"portfolio_values = asset_1_prices + asset_2_prices + asset_3_prices + np.random.randn(num_records) * 10\n", | ||
"market_index = np.cumsum(np.random.randn(num_records)) + 1000\n", | ||
"\n", | ||
"# Create a DataFrame\n", | ||
"data = {\n", | ||
" 'timestamp': date_range,\n", | ||
" 'Asset_1': asset_1_prices,\n", | ||
" 'Asset_2': asset_2_prices,\n", | ||
" 'Asset_3': asset_3_prices,\n", | ||
" 'Portfolio_Value': portfolio_values,\n", | ||
" 'Market_Index': market_index\n", | ||
"}\n", | ||
"\n", | ||
"df = pd.DataFrame(data)\n", | ||
"\n", | ||
"# Save the DataFrame to a CSV file\n", | ||
"csv_file_path = 'financial_portfolio_data.csv'\n", | ||
"df.to_csv(csv_file_path, index=False)\n", | ||
"print(f\"Dataset saved to {csv_file_path}\")\n" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 2, | ||
"metadata": { | ||
"colab": { | ||
"base_uri": "https://localhost:8080/" | ||
}, | ||
"id": "oVNru4wBw89k", | ||
"outputId": "e51296b1-cbb4-4656-f046-bc4e73a7caa4" | ||
}, | ||
"outputs": [ | ||
{ | ||
"output_type": "stream", | ||
"name": "stdout", | ||
"text": [ | ||
" timestamp Asset_1 Asset_2 Asset_3 Portfolio_Value \\\n", | ||
"0 2020-01-01 00:00:00 100.496714 151.399355 199.324822 432.142816 \n", | ||
"1 2020-01-01 01:00:00 100.358450 152.323989 199.180303 443.258892 \n", | ||
"2 2020-01-01 02:00:00 101.006138 152.383619 198.387883 447.641586 \n", | ||
"3 2020-01-01 03:00:00 102.529168 151.736683 198.079922 471.222649 \n", | ||
"4 2020-01-01 04:00:00 102.295015 152.434906 196.186307 456.481759 \n", | ||
"\n", | ||
" Market_Index \n", | ||
"0 999.136506 \n", | ||
"1 999.105303 \n", | ||
"2 999.123320 \n", | ||
"3 999.595950 \n", | ||
"4 998.229092 \n" | ||
] | ||
} | ||
], | ||
"source": [ | ||
"# Load the dataset to preview\n", | ||
"loaded_df = pd.read_csv(csv_file_path)\n", | ||
"print(loaded_df.head())\n" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 3, | ||
"metadata": { | ||
"id": "WaIPjci2w-2-" | ||
}, | ||
"outputs": [], | ||
"source": [ | ||
"import gym\n", | ||
"from gym import spaces\n", | ||
"\n", | ||
"class PortfolioEnv(gym.Env):\n", | ||
" def __init__(self, df):\n", | ||
" super(PortfolioEnv, self).__init__()\n", | ||
" self.df = df\n", | ||
" self.current_step = 0\n", | ||
" self.action_space = spaces.Discrete(3) # Actions: 0 = hold, 1 = buy, 2 = sell\n", | ||
" self.observation_space = spaces.Box(low=-np.inf, high=np.inf, shape=(5,), dtype=np.float32)\n", | ||
" self.balance = 10000\n", | ||
" self.portfolio_value = self.balance\n", | ||
"\n", | ||
" def reset(self):\n", | ||
" self.current_step = 0\n", | ||
" self.balance = 10000\n", | ||
" self.portfolio_value = self.balance\n", | ||
" return self._next_observation()\n", | ||
"\n", | ||
" def _next_observation(self):\n", | ||
" obs = self.df.iloc[self.current_step][['Asset_1', 'Asset_2', 'Asset_3', 'Portfolio_Value', 'Market_Index']].values\n", | ||
" return obs\n", | ||
"\n", | ||
" def step(self, action):\n", | ||
" obs = self._next_observation()\n", | ||
" self.current_step += 1\n", | ||
"\n", | ||
" reward = 0\n", | ||
" if action == 1: # Buy\n", | ||
" self.balance -= obs[3] # Subtract portfolio value\n", | ||
" reward = obs[3] # Reward is the portfolio value\n", | ||
" elif action == 2: # Sell\n", | ||
" self.balance += obs[3] # Add portfolio value\n", | ||
" reward = -obs[3] # Negative reward as penalty for selling\n", | ||
"\n", | ||
" self.portfolio_value = self.balance + obs[3]\n", | ||
" done = self.current_step >= len(self.df) - 1\n", | ||
" return obs, reward, done, {}\n", | ||
"\n", | ||
" def render(self, mode='human'):\n", | ||
" print(f'Step: {self.current_step}, Balance: {self.balance}, Portfolio Value: {self.portfolio_value}')\n", | ||
"\n", | ||
"# Create the environment\n", | ||
"env = PortfolioEnv(loaded_df)\n" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 4, | ||
"metadata": { | ||
"id": "An2hCoUJxBNl" | ||
}, | ||
"outputs": [], | ||
"source": [ | ||
"from collections import deque\n", | ||
"import random\n", | ||
"\n", | ||
"class QLearningAgent:\n", | ||
" def __init__(self, state_size, action_size):\n", | ||
" self.state_size = state_size\n", | ||
" self.action_size = action_size\n", | ||
" self.memory = deque(maxlen=2000)\n", | ||
" self.gamma = 0.95\n", | ||
" self.epsilon = 1.0\n", | ||
" self.epsilon_min = 0.01\n", | ||
" self.epsilon_decay = 0.995\n", | ||
" self.learning_rate = 0.001\n", | ||
" self.q_table = {}\n", | ||
"\n", | ||
" def get_qs(self, state):\n", | ||
" return self.q_table.get(tuple(state.flatten()), np.zeros(self.action_size))\n", | ||
"\n", | ||
" def remember(self, state, action, reward, next_state, done):\n", | ||
" self.memory.append((state, action, reward, next_state, done))\n", | ||
"\n", | ||
" def act(self, state):\n", | ||
" if np.random.rand() <= self.epsilon:\n", | ||
" return random.randrange(self.action_size)\n", | ||
" qs = self.get_qs(state)\n", | ||
" return np.argmax(qs)\n", | ||
"\n", | ||
" def replay(self, batch_size):\n", | ||
" minibatch = random.sample(self.memory, batch_size)\n", | ||
" for state, action, reward, next_state, done in minibatch:\n", | ||
" target = reward\n", | ||
" if not done:\n", | ||
" target = reward + self.gamma * np.amax(self.get_qs(next_state))\n", | ||
" qs = self.get_qs(state)\n", | ||
" qs[action] = target\n", | ||
" self.q_table[tuple(state.flatten())] = qs\n", | ||
" if self.epsilon > self.epsilon_min:\n", | ||
" self.epsilon *= self.epsilon_decay\n", | ||
"\n", | ||
"# Initialize the agent\n", | ||
"state_size = env.observation_space.shape[0]\n", | ||
"action_size = env.action_space.n\n", | ||
"agent = QLearningAgent(state_size, action_size)\n", | ||
"\n", | ||
"# Train the agent\n", | ||
"episodes = 500\n", | ||
"batch_size = 32\n", | ||
"\n", | ||
"for e in range(episodes):\n", | ||
" state = env.reset()\n", | ||
" state = np.reshape(state, [1, state_size])\n", | ||
"\n", | ||
" for time in range(200):\n", | ||
" action = agent.act(state)\n", | ||
" next_state, reward, done, _ = env.step(action)\n", | ||
" next_state = np.reshape(next_state, [1, state_size])\n", | ||
"\n", | ||
" agent.remember(state, action, reward, next_state, done)\n", | ||
" state = next_state\n", | ||
"\n", | ||
" if done:\n", | ||
" print(f\"Episode: {e+1}/{episodes}, Score: {reward}, Epsilon: {agent.epsilon:.2}\")\n", | ||
" break\n", | ||
"\n", | ||
" if len(agent.memory) > batch_size:\n", | ||
" agent.replay(batch_size)\n" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 5, | ||
"metadata": { | ||
"id": "OmRsM0J8xGHQ" | ||
}, | ||
"outputs": [], | ||
"source": [ | ||
"# Evaluate the agent\n", | ||
"state = env.reset()\n", | ||
"state = np.reshape(state, [1, state_size])\n", | ||
"total_reward = 0\n", | ||
"\n", | ||
"for time in range(500):\n", | ||
" action = agent.act(state)\n", | ||
" next_state, reward, done, _ = env.step(action)\n", | ||
" next_state = np.reshape(next_state, [1, state_size])\n", | ||
" total_reward += reward\n", | ||
" state = next_state\n", | ||
"\n", | ||
" if done:\n", | ||
" print(f\"Final Balance: {total_reward}\")\n", | ||
" break\n" | ||
] | ||
} | ||
], | ||
"metadata": { | ||
"colab": { | ||
"provenance": [] | ||
}, | ||
"kernelspec": { | ||
"display_name": "Python 3", | ||
"name": "python3" | ||
}, | ||
"language_info": { | ||
"name": "python" | ||
} | ||
}, | ||
"nbformat": 4, | ||
"nbformat_minor": 0 | ||
} |
Oops, something went wrong.