vansh0003 commited on
Commit
196382a
·
verified ·
1 Parent(s): 3bd221f

Upload 42 files

Browse files
Files changed (42) hide show
  1. Assets/logo.png +0 -0
  2. README.md +20 -19
  3. __pycache__/config.cpython-314.pyc +0 -0
  4. __pycache__/utils.cpython-314.pyc +0 -0
  5. app.py +338 -0
  6. config.py +327 -0
  7. models/Target_Return_10D/Cipla.pkl +3 -0
  8. models/Target_Return_10D/HDFC_Bank.pkl +3 -0
  9. models/Target_Return_10D/Hindustan_Unilever.pkl +3 -0
  10. models/Target_Return_10D/ICICI_Bank.pkl +3 -0
  11. models/Target_Return_10D/ITC.pkl +3 -0
  12. models/Target_Return_10D/Infosys.pkl +3 -0
  13. models/Target_Return_10D/ONGC.pkl +3 -0
  14. models/Target_Return_10D/Reliance.pkl +3 -0
  15. models/Target_Return_10D/Sun_Pharma.pkl +3 -0
  16. models/Target_Return_10D/TCS.pkl +3 -0
  17. models/Target_Return_5D/Cipla.pkl +3 -0
  18. models/Target_Return_5D/HDFC_Bank.pkl +3 -0
  19. models/Target_Return_5D/Hindustan_Unilever.pkl +3 -0
  20. models/Target_Return_5D/ICICI_Bank.pkl +3 -0
  21. models/Target_Return_5D/ITC.pkl +3 -0
  22. models/Target_Return_5D/Infosys.pkl +3 -0
  23. models/Target_Return_5D/ONGC.pkl +3 -0
  24. models/Target_Return_5D/Reliance.pkl +3 -0
  25. models/Target_Return_5D/Sun_Pharma.pkl +3 -0
  26. models/Target_Return_5D/TCS.pkl +3 -0
  27. models/a2c_dynamic_allocator_final.zip +3 -0
  28. models/correlation_matrix.pkl +3 -0
  29. models/market_regime_model.pkl +3 -0
  30. models/market_scaler.pkl +3 -0
  31. models/ppo_dynamic_allocator_final.zip +3 -0
  32. models/ppo_dynamic_allocator_final300.zip +3 -0
  33. models/ppo_dynamic_allocator_v1.zip +3 -0
  34. models/ppo_dynamic_allocator_v2.zip +3 -0
  35. models/ppo_dynamic_allocator_v3.zip +3 -0
  36. models/price_data.pkl +3 -0
  37. models/regime_map.json +8 -0
  38. models/rl_feature_data.pkl +3 -0
  39. models/stock_cluster_model.pkl +3 -0
  40. models/stock_scaler.pkl +3 -0
  41. requirements.txt +14 -3
  42. utils.py +334 -0
Assets/logo.png ADDED
README.md CHANGED
@@ -1,19 +1,20 @@
1
- ---
2
- title: Dynamic Allocation System V1
3
- emoji: 🚀
4
- colorFrom: red
5
- colorTo: red
6
- sdk: docker
7
- app_port: 8501
8
- tags:
9
- - streamlit
10
- pinned: false
11
- short_description: Streamlit template space
12
- ---
13
-
14
- # Welcome to Streamlit!
15
-
16
- Edit `/src/streamlit_app.py` to customize this app to your heart's desire. :heart:
17
-
18
- If you have any questions, checkout our [documentation](https://docs.streamlit.io) and [community
19
- forums](https://discuss.streamlit.io).
 
 
1
+ # Dynamic Allocation System Operational Backtest Freeze File Blueprint
2
+ # Dynamic Portfolio Allocation System
3
+
4
+ A machine learning based portfolio allocation system using:
5
+
6
+ - Feature Engineering
7
+ - Gradient Boosting
8
+ - PPO Reinforcement Learning
9
+
10
+ ## Features
11
+
12
+ - Dynamic portfolio allocation
13
+ - Risk profile selection
14
+ - 5-day and 10-day investment horizon
15
+ - Cash reserve recommendation
16
+ - Equal Weight comparison
17
+ - Portfolio insights
18
+ - Interactive dashboard using Streamlit
19
+
20
+ Developed as a Final Year Project.
__pycache__/config.cpython-314.pyc ADDED
Binary file (4.54 kB). View file
 
__pycache__/utils.cpython-314.pyc ADDED
Binary file (4.91 kB). View file
 
app.py ADDED
@@ -0,0 +1,338 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import pandas as pd
3
+ import plotly.express as px
4
+ import plotly.graph_objects as go
5
+
6
+ from utils import generate_recommendation
7
+ from config import DEFAULT_BUDGET,ALL_STOCKS
8
+
9
+ # Page Config
10
+ st.set_page_config(
11
+ page_title="Dynamic Allocation System",
12
+ page_icon="📈",
13
+ layout="wide",
14
+ initial_sidebar_state="expanded"
15
+ )
16
+
17
+ # Custom Styling
18
+ st.markdown("""
19
+ <style>
20
+ .main {
21
+ background-color:#0e1117;
22
+ }
23
+ .block-container {
24
+ padding-top:2rem;
25
+ }
26
+ div[data-testid="metric-container"] {
27
+ background:#1b1f2a;
28
+ border-radius:15px;
29
+ padding:15px;
30
+ border:1px solid #2d3748;
31
+ }
32
+ .stButton>button {
33
+ width:100%;
34
+ border-radius:10px;
35
+ height:55px;
36
+ font-size:18px;
37
+ font-weight:bold;
38
+ }
39
+ </style>
40
+ """, unsafe_allow_html=True)
41
+
42
+ # Main Title & Status Badges
43
+ st.title("📈 Dynamic Allocation System")
44
+
45
+ badge_col1, badge_col2 = st.columns([1, 5])
46
+ with badge_col1:
47
+ st.success("🟢 Model Status: Loaded")
48
+ with badge_col2:
49
+ st.info("🤖 Engine: PPO Reinforcement Learning")
50
+
51
+ st.caption("Machine Learning Based Dynamic Allocation System using Proximal Policy Optimization (PPO)")
52
+
53
+
54
+ # Clean fallback safety line
55
+
56
+ # Sidebar Setup
57
+ st.sidebar.image("Assets/logo.png", use_container_width=True)
58
+ st.sidebar.header("Investment Settings")
59
+
60
+ budget = st.sidebar.number_input(
61
+ "Investment Budget (₹)",
62
+ min_value=10000,
63
+ max_value=10000000,
64
+ value=DEFAULT_BUDGET,
65
+ step=10000
66
+ )
67
+
68
+ risk = st.sidebar.selectbox(
69
+ "Risk Profile",
70
+ ["Conservative", "Moderate", "Aggressive"]
71
+ )
72
+
73
+ horizon = st.sidebar.radio(
74
+ "Investment Horizon (Days)",
75
+ [5, 10]
76
+ )
77
+
78
+ st.sidebar.divider()
79
+
80
+ # Updated Model Sidebar (Pipelines and Architecture Overview)
81
+ st.sidebar.info(f"""
82
+ ### Model Pipeline
83
+
84
+ 📊 Feature Engineering
85
+
86
+ ⬇️
87
+
88
+ 🤖 Gradient Boosting Return Prediction
89
+
90
+ ⬇️
91
+
92
+ 🧠 PPO Reinforcement Learning
93
+
94
+ ⬇️
95
+
96
+ 💼 Dynamic Portfolio Allocation
97
+
98
+ ---
99
+
100
+ **Universe:** 10 Indian Stocks
101
+
102
+ **Data Window:** Historical Static Dataset
103
+
104
+ **Future Enhancement:** Live Market Data Integration
105
+ """)
106
+
107
+ generate = st.sidebar.button("🚀 Optimize Portfolio", use_container_width=True)
108
+
109
+ # Main Application Logic
110
+ if generate:
111
+ with st.spinner("Optimizing Portfolio..."):
112
+ # Fetch Recommendations
113
+ allocation, cash, summary, weights, snapshot_date = generate_recommendation(budget, risk, horizon)
114
+
115
+ # Data Cleaning, Sorting & Precision Preprocessing
116
+ allocation = allocation.sort_values("Weight (%)", ascending=False).reset_index(drop=True)
117
+ allocation["Investment (₹)"] = allocation["Investment (₹)"].round(0)
118
+ allocation["Weight (%)"] = allocation["Weight (%)"].round(2)
119
+
120
+ # Asset Allocation Descriptive Labels
121
+ allocation["Recommendation"] = allocation["Weight (%)"].apply(
122
+ lambda x: "🟢 High Allocation" if x >= 15 else ("🟡 Medium Allocation" if x >= 7 else "⚪ Low Allocation")
123
+ )
124
+
125
+ # 1. Adaptable Universe Scaling: Calculate purely from returned dataset dimensions
126
+ total_universe = len(allocation)
127
+ allocated_count = (allocation["Weight (%)"] > 0.5).sum()
128
+
129
+ # KPI Metric Cards
130
+ col1, col2, col3, col4 = st.columns(4)
131
+ col1.metric("💰 Investment Budget", f"₹{budget:,.0f}")
132
+ col2.metric("📈 Recommended Investment", f"₹{summary['Total Investment']:,.0f}")
133
+ col3.metric("💵 Cash Reserve", f"₹{cash:,.0f}")
134
+ col4.metric("📊 Stocks Allocated", f"{allocated_count} / {total_universe}")
135
+
136
+ # Split Layout: Pie Chart and Recommendation Summary Panels
137
+ left, right = st.columns([3, 2])
138
+
139
+ with left:
140
+ st.subheader("Recommended Portfolio Allocation")
141
+ fig = px.pie(
142
+ allocation,
143
+ names="Stock",
144
+ values="Investment (₹)",
145
+ hole=0.45
146
+ )
147
+ fig.update_layout(
148
+ height=550,
149
+ legend_title="Stocks"
150
+ )
151
+ st.plotly_chart(fig, use_container_width=True, config={"displayModeBar": False})
152
+
153
+ with right:
154
+ st.subheader("Recommendation Summary")
155
+ st.success(f"💵 Cash Reserve\n\n₹{cash:,.0f}")
156
+
157
+ diversification = (allocation["Weight (%)"] > 5).sum() / total_universe
158
+ st.write("Diversification Index")
159
+ st.progress(diversification)
160
+
161
+ # Additional User Context Metrics
162
+ meta_col1, meta_col2 = st.columns(2)
163
+ with meta_col1:
164
+ st.markdown(f"**Recommendation Based On:** {snapshot_date}")
165
+ with meta_col2:
166
+ st.markdown(f"**Investment Horizon:** {horizon} Days")
167
+
168
+ # Horizontal Bar Chart (Sorted)
169
+ st.divider()
170
+ st.subheader("📊 Stock Allocation Balance")
171
+
172
+ bar_fig = px.bar(
173
+ allocation,
174
+ x="Weight (%)",
175
+ y="Stock",
176
+ orientation="h",
177
+ text="Weight (%)"
178
+ )
179
+ bar_fig.update_traces(
180
+ texttemplate="%{text:.1f} %",
181
+ textposition="outside"
182
+ )
183
+ bar_fig.update_layout(
184
+ yaxis=dict(categoryorder="total ascending"),
185
+ height=500,
186
+ xaxis_title="Portfolio Weight (%)",
187
+ yaxis_title=""
188
+ )
189
+ st.plotly_chart(bar_fig, use_container_width=True, config={"displayModeBar": False})
190
+
191
+ # PPO vs Equal Weight Comparison Strategy Chart
192
+ st.divider()
193
+ st.subheader("⚖ PPO vs Equal Weight Comparison")
194
+
195
+ comparison = allocation.copy()
196
+ comparison["Equal Weight"] = 100 / total_universe
197
+ comparison = comparison.rename(columns={"Weight (%)": "PPO"})
198
+
199
+ comparison_long = comparison.melt(
200
+ id_vars="Stock",
201
+ value_vars=["PPO", "Equal Weight"],
202
+ var_name="Strategy",
203
+ value_name="Weight"
204
+ )
205
+
206
+ comp_fig = px.bar(
207
+ comparison_long,
208
+ x="Stock",
209
+ y="Weight",
210
+ color="Strategy",
211
+ barmode="group",
212
+ text="Weight"
213
+ )
214
+ comp_fig.update_traces(
215
+ texttemplate="%{text:.1f}%",
216
+ textposition="outside"
217
+ )
218
+ st.plotly_chart(comp_fig, use_container_width=True, config={"displayModeBar": False})
219
+ st.caption("_Equal Weight assigns an identical fixed allocation to every stock, while the PPO Reinforcement Learning agent dynamically adjusts allocations based on learned market risk patterns and current portfolio states._")
220
+
221
+ # Structural Asset Allocation Statistics
222
+ st.divider()
223
+ c1, c2, c3 = st.columns(3)
224
+ c1.metric("Largest Allocation", f"{allocation.iloc[0]['Weight (%)']:.1f}%")
225
+ c2.metric("Average Allocation", f"{allocation['Weight (%)'].mean():.1f}%")
226
+ c3.metric("Diversified Stocks (>5% Weight)", (allocation["Weight (%)"] > 5).sum())
227
+
228
+ # Cleaned Standard Header Title "Recommended Portfolio"
229
+ st.divider()
230
+ st.subheader("📋 Recommended Portfolio")
231
+
232
+ table_display = allocation[["Stock", "Weight (%)", "Investment (₹)", "Recommendation"]].copy()
233
+ cash_weight = (cash / budget) * 100
234
+ cash_row = pd.DataFrame([{
235
+ "Stock": "💵 Cash Reserve",
236
+ "Weight (%)": round(cash_weight, 2),
237
+ "Investment (₹)": round(cash, 0),
238
+ "Recommendation": "Liquidity Reserve"
239
+ }])
240
+ table_display = pd.concat([table_display, cash_row], ignore_index=True)
241
+
242
+ st.dataframe(
243
+ table_display,
244
+ use_container_width=True,
245
+ hide_index=True
246
+ )
247
+
248
+ # Highlight Top Asset Pick
249
+ st.divider()
250
+ top = allocation.iloc[0]
251
+ st.success(f"""
252
+ ## ⭐ Top Recommendation
253
+ ### {top['Stock']}
254
+
255
+ Recommended Investment
256
+ ### ₹{top['Investment (₹)']:,.0f}
257
+
258
+ Portfolio Weight
259
+ ### {top['Weight (%)']:.2f}%
260
+
261
+ Allocation Strategy
262
+ ### {top['Recommendation']}
263
+ """)
264
+
265
+ # Dynamic Portfolio Insights Panel
266
+ st.divider()
267
+ st.subheader("💡 Dynamic Portfolio Insights")
268
+
269
+ diversified_count = (allocation["Weight (%)"] > 5).sum()
270
+ overweighted_df = allocation[allocation["Weight (%)"] > (100 / total_universe)]
271
+ overweighted_names = overweighted_df["Stock"].head(3).tolist()
272
+ overweighted_str = ", ".join(overweighted_names) if overweighted_names else "None"
273
+
274
+ st.write(f"""
275
+ * 📈 **Highest Allocation Asset:** **{top['Stock']}** received the highest portfolio allocation from the PPO agent at **{top['Weight (%)']:.2f}%**.
276
+ * 📉 **Lowest Allocation Asset:** **{allocation.iloc[-1]['Stock']}** has the lowest framework allocation at **{allocation.iloc[-1]['Weight (%)']:.2f}%**.
277
+ * 💵 **Liquidity Management:** Cash balance retention is securely held at **₹{cash:,.0f}** as a tactical Liquidity Reserve.
278
+ * 🧩 **Diversification Scope:** **{diversified_count} out of {total_universe}** asset blocks successfully crossed the 5% concentration limit index.
279
+ * 🧠 **RL Comparison Profile:** Compared with an equal-weight strategy, the PPO agent allocated larger weights to: **{overweighted_str}**.
280
+ """)
281
+
282
+ # Capital Utilization Gauge Graphic
283
+ st.divider()
284
+ st.subheader("💰 Capital Utilization")
285
+
286
+ invested = summary["Total Investment"]
287
+ utilization_percent = (invested / budget) * 100
288
+
289
+ gauge_fig = go.Figure(
290
+ go.Indicator(
291
+ mode="gauge+number",
292
+ value=utilization_percent,
293
+ number={"suffix": "%"},
294
+ title={"text": f"₹{invested:,.0f} / ₹{budget:,.0f}"},
295
+ gauge={
296
+ "axis": {"range": [0, 100]},
297
+ "bar": {"color": "#10B981"}
298
+ }
299
+ )
300
+ )
301
+ gauge_fig.update_layout(height=350)
302
+ st.plotly_chart(gauge_fig, use_container_width=True, config={"displayModeBar": False})
303
+
304
+ # Natural Download Branding Action String
305
+ st.divider()
306
+ csv = table_display.to_csv(index=False)
307
+ st.download_button(
308
+ label="📥 Download Recommendation",
309
+ data=csv,
310
+ file_name="Dynamic_Allocation_Recommendation.csv",
311
+ mime="text/csv",
312
+ use_container_width=True
313
+ )
314
+
315
+ else:
316
+ # Humanized Welcome Screen Layout
317
+ st.info("""
318
+ 👋 **Welcome to the Dynamic Allocation System.** Configure your investment preferences using the sidebar and click **"Optimize Portfolio"** to receive a personalized portfolio allocation generated by the PPO reinforcement learning agent.
319
+ """)
320
+
321
+ # Polished Software About Layout View
322
+ st.markdown("<br>", unsafe_allow_html=True)
323
+ with st.expander("ℹ️ About This Project"):
324
+ st.write(f"""
325
+ This application demonstrates a machine learning-based dynamic portfolio allocation system.
326
+
327
+ The pipeline consists of:
328
+ * **Feature Engineering Framework:** Extracts trend, momentum, and risk indicators.
329
+ * **Gradient Boosting Prediction Engine:** Estimates expected asset trajectory behaviors.
330
+ * **PPO Reinforcement Learning Strategy:** Evaluates continuous state transitions to optimize portfolio allocations.
331
+
332
+ ---
333
+ * **Deployment Status:** Historical Static Dataset for Reproducibility
334
+ * **System Architecture:** Pipeline Integration v1.0
335
+
336
+ _Future versions will support live market data streaming and programmatic broker APIs. Recommendations are generated from the historical dataset.._
337
+ """)
338
+
config.py ADDED
@@ -0,0 +1,327 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ ============================================================
3
+ AI Dynamic Portfolio Management System
4
+ Project Configuration
5
+ ============================================================
6
+
7
+ Author : Vansh Putalu
8
+ Version : 2.0
9
+ Purpose : Global configuration shared across all notebooks
10
+ """
11
+
12
+ import os
13
+
14
+ # ============================================================
15
+ # PROJECT PATHS
16
+ # ============================================================
17
+
18
+ PROJECT_ROOT = os.path.dirname(os.path.abspath(__file__))
19
+
20
+ DATA_PATH = os.path.join(PROJECT_ROOT, "data")
21
+
22
+ RAW_PATH = os.path.join(DATA_PATH, "raw")
23
+
24
+ PROCESSED_PATH = os.path.join(DATA_PATH, "processed")
25
+
26
+ FEATURE_PATH = os.path.join(DATA_PATH, "features")
27
+
28
+ MODEL_PATH = os.path.join(PROJECT_ROOT, "models")
29
+
30
+ RESULT_PATH = os.path.join(PROJECT_ROOT, "results")
31
+
32
+ IMAGE_PATH = os.path.join(RESULT_PATH, "figures")
33
+
34
+ REPORT_PATH = os.path.join(PROJECT_ROOT, "reports")
35
+
36
+ # ============================================================
37
+ # RAW DATA FOLDERS
38
+ # ============================================================
39
+
40
+ STOCK_PATH = os.path.join(RAW_PATH, "stocks")
41
+
42
+ MARKET_PATH = os.path.join(RAW_PATH, "market")
43
+
44
+ MACRO_PATH = os.path.join(RAW_PATH, "macro")
45
+
46
+ SECTOR_PATH = os.path.join(RAW_PATH, "sector")
47
+
48
+ METADATA_PATH = os.path.join(RAW_PATH, "metadata")
49
+
50
+ # ============================================================
51
+ # CREATE DIRECTORIES
52
+ # ============================================================
53
+
54
+ DIRECTORIES = [
55
+
56
+ DATA_PATH,
57
+
58
+ RAW_PATH,
59
+
60
+ PROCESSED_PATH,
61
+
62
+ FEATURE_PATH,
63
+
64
+ MODEL_PATH,
65
+
66
+ RESULT_PATH,
67
+
68
+ IMAGE_PATH,
69
+
70
+ REPORT_PATH,
71
+
72
+ STOCK_PATH,
73
+
74
+ MARKET_PATH,
75
+
76
+ MACRO_PATH,
77
+
78
+ SECTOR_PATH,
79
+
80
+ METADATA_PATH
81
+
82
+ ]
83
+
84
+ for directory in DIRECTORIES:
85
+
86
+ os.makedirs(directory, exist_ok=True)
87
+
88
+ # ============================================================
89
+ # DOWNLOAD PERIOD
90
+ # ============================================================
91
+
92
+ START_DATE = "2008-01-01"
93
+
94
+ END_DATE = "2026-12-31"
95
+
96
+ # ============================================================
97
+ # PROJECT SETTINGS
98
+ # ============================================================
99
+
100
+ RANDOM_STATE = 42
101
+
102
+ TRAIN_RATIO = 0.80
103
+
104
+ TRADING_DAYS = 252
105
+
106
+ TARGET_HORIZONS = [5, 10]
107
+
108
+ DEFAULT_TARGET = "Target_5D"
109
+
110
+ TARGET_TYPE = "Regression"
111
+
112
+ # ============================================================
113
+ # PORTFOLIO SETTINGS
114
+ # ============================================================
115
+
116
+ INITIAL_CAPITAL = 100000
117
+
118
+ MONTHLY_SIP = 10000
119
+
120
+ REBALANCE_FREQUENCY = 5
121
+
122
+ MAX_STOCK_WEIGHT = 0.30
123
+
124
+ MIN_STOCK_WEIGHT = 0.00
125
+
126
+ # ============================================================
127
+ # STOCKS
128
+ # ============================================================
129
+
130
+ ASSETS = {
131
+
132
+ "TCS":{
133
+
134
+ "ticker":"TCS.NS",
135
+
136
+ "sector":"IT"
137
+
138
+ },
139
+
140
+ "Infosys":{
141
+
142
+ "ticker":"INFY.NS",
143
+
144
+ "sector":"IT"
145
+
146
+ },
147
+
148
+ "HDFC_Bank":{
149
+
150
+ "ticker":"HDFCBANK.NS",
151
+
152
+ "sector":"Banking"
153
+
154
+ },
155
+
156
+ "ICICI_Bank":{
157
+
158
+ "ticker":"ICICIBANK.NS",
159
+
160
+ "sector":"Banking"
161
+
162
+ },
163
+
164
+ "Reliance":{
165
+
166
+ "ticker":"RELIANCE.NS",
167
+
168
+ "sector":"Energy"
169
+
170
+ },
171
+
172
+ "ONGC":{
173
+
174
+ "ticker":"ONGC.NS",
175
+
176
+ "sector":"Energy"
177
+
178
+ },
179
+
180
+ "ITC":{
181
+
182
+ "ticker":"ITC.NS",
183
+
184
+ "sector":"FMCG"
185
+
186
+ },
187
+
188
+ "Hindustan_Unilever":{
189
+
190
+ "ticker":"HINDUNILVR.NS",
191
+
192
+ "sector":"FMCG"
193
+
194
+ },
195
+
196
+ "Sun_Pharma":{
197
+
198
+ "ticker":"SUNPHARMA.NS",
199
+
200
+ "sector":"Pharma"
201
+
202
+ },
203
+
204
+ "Cipla":{
205
+
206
+ "ticker":"CIPLA.NS",
207
+
208
+ "sector":"Pharma"
209
+
210
+ }
211
+
212
+ }
213
+
214
+ ALL_STOCKS = list(ASSETS.keys())
215
+
216
+ # ============================================================
217
+ # INDIAN MARKET
218
+ # ============================================================
219
+
220
+ MARKET_INDICES = {
221
+
222
+ "NIFTY50":"^NSEI",
223
+
224
+ "BANKNIFTY":"^NSEBANK",
225
+
226
+ "INDIA_VIX":"^INDIAVIX"
227
+
228
+ }
229
+
230
+ # ============================================================
231
+ # GLOBAL MACRO
232
+ # ============================================================
233
+
234
+ GLOBAL_MACRO = {
235
+
236
+ "NASDAQ":"^IXIC",
237
+
238
+ "SP500":"^GSPC",
239
+
240
+ "DOWJONES":"^DJI",
241
+
242
+ "USDINR":"INR=X",
243
+
244
+ "DXY":"DX-Y.NYB",
245
+
246
+ "Crude_Oil":"CL=F",
247
+
248
+ "Gold":"GC=F",
249
+
250
+ "Silver":"SI=F",
251
+
252
+ "Copper":"HG=F"
253
+
254
+ }
255
+
256
+ # ============================================================
257
+ # SECTOR INDICES
258
+ # ============================================================
259
+
260
+ SECTOR_INDICES = {
261
+
262
+ "NIFTY_IT":"^CNXIT",
263
+
264
+ "NIFTY_PHARMA":"^CNXPHARMA",
265
+
266
+ "NIFTY_FMCG":"^CNXFMCG",
267
+
268
+ "NIFTY_ENERGY":"^CNXENERGY",
269
+
270
+ "NIFTY_BANK":"^NSEBANK"
271
+
272
+ }
273
+
274
+ # ============================================================
275
+ # PROJECT SUMMARY
276
+ # ============================================================
277
+
278
+ TOTAL_STOCKS = len(ASSETS)
279
+
280
+ TOTAL_MARKET = len(MARKET_INDICES)
281
+
282
+ TOTAL_MACRO = len(GLOBAL_MACRO)
283
+
284
+ TOTAL_SECTOR = len(SECTOR_INDICES)
285
+
286
+ TOTAL_DOWNLOADS = (
287
+
288
+ TOTAL_STOCKS +
289
+
290
+ TOTAL_MARKET +
291
+
292
+ TOTAL_MACRO +
293
+
294
+ TOTAL_SECTOR
295
+
296
+ )
297
+
298
+ RISK_MAPPING = {
299
+
300
+ "Conservative": 0,
301
+ "Moderate": 1,
302
+ "Aggressive": 2
303
+
304
+ }
305
+
306
+ HORIZON_MAPPING = {
307
+
308
+ "5 Days": 5,
309
+ "10 Days": 10
310
+
311
+ }
312
+
313
+ MODEL_FOLDER = "Models"
314
+
315
+ PPO_PATH = "Models/ppo_dynamic_allocator_final300"
316
+
317
+ MODEL_5D = "Models/TargetReturn_5D"
318
+
319
+ MODEL_10D = "Models/TargetReturn_10D"
320
+
321
+ PRICE_DATA = "Models/price_data.pkl"
322
+
323
+ RL_FEATURE_DATA = "Models/rl_feature_data.pkl"
324
+
325
+ CORRELATION_MATRIX = "Models/correlation_matrix.pkl"
326
+
327
+ DEFAULT_BUDGET = 500000
models/Target_Return_10D/Cipla.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:850acfc3d69c48c80b4bd9d0b5dba219607cf41cbb4b323f6c5c87a3abd38d2d
3
+ size 4982553
models/Target_Return_10D/HDFC_Bank.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:035fc323a430562d459416b1cbae42f323c233d4096b25934073d2632a21cfbe
3
+ size 1192699
models/Target_Return_10D/Hindustan_Unilever.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d613a887258d8445cb464f9bcac5349bea01055069294ac5c9e64149ad602aa6
3
+ size 11699384
models/Target_Return_10D/ICICI_Bank.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d0739096285fb92e063512c7db6c7696eb25938c6d5f913d005b34926ed3dc08
3
+ size 263672
models/Target_Return_10D/ITC.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:90f1b8b3a2975b0e9475768e36910110aafe86c41de45494e922d5d017774fe5
3
+ size 4998809
models/Target_Return_10D/Infosys.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:250c218b26dbbd99a1f9d921507593a77ded88a9a87b0aff6d53c7a63d7ba5f3
3
+ size 13008680
models/Target_Return_10D/ONGC.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:9b4719dc0d2eaca75d450ea67ca363b21ad9afc6007cf602a94adbba382b2134
3
+ size 105568
models/Target_Return_10D/Reliance.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:f9da3411b4c0d2204783d62dd8b45bbeb61ab4f6b9720ded3675a432725d5c5f
3
+ size 4998537
models/Target_Return_10D/Sun_Pharma.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:b2a27bd03af4feb4d671632e1cd361339e807dc325d830e992caec77a6b219de
3
+ size 1365383
models/Target_Return_10D/TCS.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:cdbaf5f7bac4b73ba3a17da738ebe737f39242272293119554184db480ad9939
3
+ size 264136
models/Target_Return_5D/Cipla.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:695aa46d7ca51f7ed3ff143a8848c143ac111d9f8c3a5696e317543fc364dc59
3
+ size 316778
models/Target_Return_5D/HDFC_Bank.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a62336da25e8bff7d235098ae82618fd884cd863bac369fc944ced689e77ba76
3
+ size 376473
models/Target_Return_5D/Hindustan_Unilever.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:ded87e45c58fcf157e54a4be5aaac0165ba37147c2f6054eec4dd37a7a042c84
3
+ size 8860441
models/Target_Return_5D/ICICI_Bank.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:bbb719d157e666d3af5ad882d208da341130ac6afc954999fd2c202e4876485a
3
+ size 4998537
models/Target_Return_5D/ITC.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:400bb9b179ab8113adb6b5e08ac916032f97daa0ae78cd6c47d4b2c184e3f51b
3
+ size 271611253
models/Target_Return_5D/Infosys.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:197c4ba7f4861445a471fd46a9ebb029aa2619f29ea59943e8d1408d4e07a6cc
3
+ size 4999097
models/Target_Return_5D/ONGC.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:5338163aeb30dce732c926bf8127e44686887951b7f74de9ebe8e2877f8c2b3e
3
+ size 233321
models/Target_Return_5D/Reliance.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:0e135d6b185b244bc6780ba75a3ecffe64be6a686eeac2661f294024d9ff0910
3
+ size 4998865
models/Target_Return_5D/Sun_Pharma.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:165a7752a21e00f74b2f7062258673e6fd63392eb68f3f4d5617366a0a78d410
3
+ size 264184
models/Target_Return_5D/TCS.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:34396c4e7a667ae130b94e3c182f05aaa8b8a582052726424a9bf1be595a1ddf
3
+ size 105896
models/a2c_dynamic_allocator_final.zip ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:141afe12bdad44984a0c271a2eb5e8b0bed45bccc53280132c916d0de5d93ea8
3
+ size 4905905
models/correlation_matrix.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a6ca59e325045e43889723aea69f6293c46cff7044598fbb827b0c3280120ff4
3
+ size 1025
models/market_regime_model.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e4c1843f4f0cacac0a8bb6964945b77ddd48ab43d036355e9a0489de40d7b2b4
3
+ size 19327
models/market_scaler.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:9592bfd5b1a482079682d9f1c83ed796cc880dd1969629cb2fc4b75de3f2a808
3
+ size 1191
models/ppo_dynamic_allocator_final.zip ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:1b954ec4d9bc4fc64ee9a9d6b7b2020ebd1b8bbcf15f68950ca89cd4c550d0ce
3
+ size 7304082
models/ppo_dynamic_allocator_final300.zip ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:f98b125be942e2ff6709fc9eda8cf542951fce5066d0991404c09bc13bf94248
3
+ size 7304366
models/ppo_dynamic_allocator_v1.zip ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:989dad70d43a84d90b919e750b9b537ddb2a7e6eb88af90731cf05a1610821b5
3
+ size 7300407
models/ppo_dynamic_allocator_v2.zip ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:53ce73da0ba2245bef0694783743d0990f8d3c35f20fc3da5ecb9eafe755dbfa
3
+ size 7302807
models/ppo_dynamic_allocator_v3.zip ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:6b6c6f7dfcdfc1f7a7de03e63f9723fd256ea5ea5691e1fe8d211e7ca07d560b
3
+ size 7302392
models/price_data.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e65ced11442a27ab3346a94623dc5575fe150bac54d5305ade4c4251d0c009ff
3
+ size 143101
models/regime_map.json ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "0": "Bull Market",
3
+ "1": "Normal Market",
4
+ "2": "Global Correction",
5
+ "3": "Global Recovery",
6
+ "4": "Bear Market",
7
+ "5": "Strong Bull Rally"
8
+ }
models/rl_feature_data.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:fb7544bddd127a10b82a7a691b0dad5fe340fa397b90e19bc52537a98ac2a2c4
3
+ size 31631895
models/stock_cluster_model.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:541b5c1b9c44d1cf21dad2006bd347b31b17f9b52ab3b6e69a38ca53a2e026e2
3
+ size 1263
models/stock_scaler.pkl ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e67d6d996cadd8260b41b8468ea364c8a542032c5c4cc6f8a6da24685444cc42
3
+ size 1175
requirements.txt CHANGED
@@ -1,3 +1,14 @@
1
- altair
2
- pandas
3
- streamlit
 
 
 
 
 
 
 
 
 
 
 
 
1
+ streamlit>=1.45
2
+ pandas
3
+ numpy
4
+ plotly
5
+ scikit-learn
6
+ joblib
7
+ stable-baselines3
8
+ gymnasium
9
+ torch
10
+ xgboost
11
+ lightgbm
12
+ yfinance
13
+ matplotlib
14
+ scipy
utils.py ADDED
@@ -0,0 +1,334 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # =====================================================
2
+ # utils.py
3
+ # Backend Functions for Portfolio Allocation
4
+ # =====================================================
5
+
6
+ import os
7
+ import joblib
8
+ import numpy as np
9
+ import pandas as pd
10
+
11
+ from stable_baselines3 import PPO
12
+
13
+ from config import (
14
+ PPO_PATH,
15
+ RL_FEATURE_DATA,
16
+ PRICE_DATA,
17
+ CORRELATION_MATRIX,
18
+ ALL_STOCKS,
19
+ RISK_MAPPING
20
+ )
21
+
22
+ # =====================================================
23
+ # Load PPO Model
24
+ # =====================================================
25
+
26
+ def load_ppo():
27
+
28
+ model = PPO.load(PPO_PATH)
29
+
30
+ return model
31
+
32
+ # =====================================================
33
+ # Load Deployment Data
34
+ # =====================================================
35
+
36
+ def load_data():
37
+
38
+ rl_feature_data = joblib.load(RL_FEATURE_DATA)
39
+
40
+ price_data = joblib.load(PRICE_DATA)
41
+
42
+ correlation_matrix = joblib.load(CORRELATION_MATRIX)
43
+
44
+ return (
45
+
46
+ rl_feature_data,
47
+
48
+ price_data,
49
+
50
+ correlation_matrix
51
+
52
+ )
53
+
54
+ # =====================================================
55
+ # Latest Feature Vector
56
+ # =====================================================
57
+
58
+ def get_latest_feature_matrix(
59
+ rl_feature_data
60
+ ):
61
+
62
+ feature_matrix = []
63
+
64
+ for stock in ALL_STOCKS:
65
+
66
+ latest = (
67
+
68
+ rl_feature_data[stock]
69
+
70
+ .iloc[-1]
71
+
72
+ .values
73
+
74
+ .astype(np.float32)
75
+
76
+ )
77
+
78
+ feature_matrix.extend(latest)
79
+
80
+ return np.array(
81
+ feature_matrix,
82
+ dtype=np.float32
83
+ )
84
+
85
+ def build_observation(
86
+
87
+ rl_feature_data,
88
+
89
+ budget,
90
+
91
+ risk_profile,
92
+
93
+ investment_horizon
94
+
95
+ ):
96
+
97
+ feature_matrix = get_latest_feature_matrix(
98
+ rl_feature_data
99
+ )
100
+
101
+ shares = np.zeros(
102
+ len(ALL_STOCKS),
103
+ dtype=np.float32
104
+ )
105
+
106
+ cash = budget
107
+
108
+ portfolio_value = budget
109
+
110
+ if budget < 100000:
111
+
112
+ tier = 0
113
+
114
+ elif budget < 500000:
115
+
116
+ tier = 1
117
+
118
+ else:
119
+
120
+ tier = 2
121
+
122
+ portfolio_state = np.array(
123
+
124
+ [
125
+
126
+ budget,
127
+
128
+ tier,
129
+
130
+ cash,
131
+
132
+ RISK_MAPPING[risk_profile],
133
+
134
+ investment_horizon,
135
+
136
+ portfolio_value
137
+
138
+ ],
139
+
140
+ dtype=np.float32
141
+
142
+ )
143
+
144
+ observation = np.concatenate(
145
+
146
+ [
147
+
148
+ feature_matrix,
149
+
150
+ shares,
151
+
152
+ portfolio_state
153
+
154
+ ]
155
+
156
+ )
157
+
158
+ return observation
159
+
160
+ def predict_portfolio(
161
+
162
+ model,
163
+
164
+ observation
165
+
166
+ ):
167
+
168
+ action, _ = model.predict(
169
+
170
+ observation,
171
+
172
+ deterministic=True
173
+
174
+ )
175
+
176
+ action = np.clip(
177
+
178
+ action,
179
+
180
+ 0,
181
+
182
+ None
183
+
184
+ )
185
+
186
+ if action.sum() == 0:
187
+
188
+ action += 1
189
+
190
+ weights = action / action.sum()
191
+
192
+ return weights
193
+
194
+ def generate_portfolio(
195
+
196
+ weights,
197
+
198
+ budget
199
+
200
+ ):
201
+
202
+ allocation = pd.DataFrame({
203
+
204
+ "Stock": ALL_STOCKS,
205
+
206
+ "Weight (%)": weights[:-1] * 100,
207
+
208
+ "Investment (₹)": weights[:-1] * budget
209
+
210
+ })
211
+
212
+ allocation = allocation.sort_values(
213
+
214
+ "Weight (%)",
215
+
216
+ ascending=False
217
+
218
+ )
219
+
220
+ allocation.reset_index(
221
+
222
+ drop=True,
223
+
224
+ inplace=True
225
+
226
+ )
227
+
228
+ return allocation
229
+
230
+ def cash_remaining(
231
+
232
+ weights,
233
+
234
+ budget
235
+
236
+ ):
237
+
238
+ return weights[-1] * budget
239
+
240
+ def portfolio_summary(
241
+
242
+ weights,
243
+
244
+ budget
245
+
246
+ ):
247
+
248
+ return {
249
+
250
+ "Total Investment":
251
+
252
+ budget -
253
+
254
+ cash_remaining(
255
+
256
+ weights,
257
+
258
+ budget
259
+
260
+ ),
261
+
262
+ "Cash":
263
+
264
+ cash_remaining(
265
+
266
+ weights,
267
+
268
+ budget
269
+
270
+ ),
271
+
272
+ "Number of Stocks":
273
+
274
+ np.sum(
275
+
276
+ weights[:-1] > 0
277
+
278
+ )
279
+
280
+ }
281
+
282
+ # =====================================================
283
+ # Main Recommendation Pipeline
284
+ # =====================================================
285
+
286
+ def generate_recommendation(
287
+ budget,
288
+ risk_profile,
289
+ investment_horizon
290
+ ):
291
+
292
+ # Load everything
293
+ model = load_ppo()
294
+
295
+ rl_feature_data, price_data, correlation_matrix = load_data()
296
+
297
+ snapshot_date = (
298
+ pd.to_datetime(
299
+ next(iter(rl_feature_data.values())).index[-1]
300
+ ).strftime("%d-%b-%Y"))
301
+
302
+ # Build PPO observation
303
+ observation = build_observation(
304
+ rl_feature_data=rl_feature_data,
305
+ budget=budget,
306
+ risk_profile=risk_profile,
307
+ investment_horizon=investment_horizon
308
+ )
309
+
310
+ # PPO prediction
311
+ weights = predict_portfolio(
312
+ model,
313
+ observation
314
+ )
315
+
316
+ # Portfolio table
317
+ allocation = generate_portfolio(
318
+ weights,
319
+ budget
320
+ )
321
+
322
+ # Cash
323
+ cash = cash_remaining(
324
+ weights,
325
+ budget
326
+ )
327
+
328
+ # Summary
329
+ summary = portfolio_summary(
330
+ weights,
331
+ budget
332
+ )
333
+
334
+ return allocation, cash, summary, weights, snapshot_date