QuantResearch 2.1__tar.gz → 2.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {quantresearch-2.1 → quantresearch-2.3}/PKG-INFO +2 -2
- quantresearch-2.3/QuantResearch/__init__.py +2 -0
- {quantresearch-2.1 → quantresearch-2.3}/QuantResearch/dashboard.py +215 -222
- {quantresearch-2.1 → quantresearch-2.3}/QuantResearch.egg-info/PKG-INFO +2 -2
- {quantresearch-2.1 → quantresearch-2.3}/QuantResearch.egg-info/top_level.txt +1 -0
- {quantresearch-2.1 → quantresearch-2.3}/pyproject.toml +2 -2
- {quantresearch-2.1 → quantresearch-2.3}/setup.py +1 -1
- quantresearch-2.1/QuantResearch/__init__.py +0 -76
- {quantresearch-2.1 → quantresearch-2.3}/LICENSE +0 -0
- {quantresearch-2.1 → quantresearch-2.3}/QuantResearch/indicators.py +0 -0
- {quantresearch-2.1 → quantresearch-2.3}/QuantResearch/visualize.py +0 -0
- {quantresearch-2.1 → quantresearch-2.3}/QuantResearch.egg-info/SOURCES.txt +0 -0
- {quantresearch-2.1 → quantresearch-2.3}/QuantResearch.egg-info/dependency_links.txt +0 -0
- {quantresearch-2.1 → quantresearch-2.3}/QuantResearch.egg-info/requires.txt +0 -0
- {quantresearch-2.1 → quantresearch-2.3}/README.md +0 -0
- {quantresearch-2.1 → quantresearch-2.3}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: QuantResearch
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3
|
|
4
4
|
Summary: Technical indicators and visualization tools for quantitative research
|
|
5
5
|
Home-page: https://github.com/vinayak1729-web/QuantR
|
|
6
6
|
Author: Vinayak ShindeVishal Mishra
|
|
@@ -9,7 +9,7 @@ License: MIT
|
|
|
9
9
|
Project-URL: Homepage, https://github.com/vinayak1729-web/QuantR
|
|
10
10
|
Project-URL: Repository, https://github.com/vinayak1729-web/QuantR
|
|
11
11
|
Project-URL: Issues, https://github.com/vinayak1729-web/QuantR/issues
|
|
12
|
-
Project-URL: Documentation, https://
|
|
12
|
+
Project-URL: Documentation, https://vinayak1729-web.github.io/QuantR/
|
|
13
13
|
Keywords: finance,trading,technical-analysis,indicators,quantitative,stocks,yfinance
|
|
14
14
|
Classifier: Development Status :: 3 - Alpha
|
|
15
15
|
Classifier: Intended Audience :: Developers
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
import tkinter as tk
|
|
3
2
|
from tkinter import ttk, messagebox
|
|
4
3
|
from tkcalendar import DateEntry
|
|
@@ -9,71 +8,19 @@ import pandas as pd
|
|
|
9
8
|
from datetime import datetime, timedelta
|
|
10
9
|
import yfinance as yf
|
|
11
10
|
import numpy as np
|
|
12
|
-
|
|
13
|
-
#
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
return (100 - (100 / (1 + (gain / loss))))
|
|
26
|
-
|
|
27
|
-
def bb_bands(price, period=20, num_std=2):
|
|
28
|
-
rolling_mean = price.rolling(window=period).mean()
|
|
29
|
-
rolling_std = price.rolling(window=period).std()
|
|
30
|
-
upper_band = rolling_mean + (rolling_std * num_std)
|
|
31
|
-
lower_band = rolling_mean - (rolling_std * num_std)
|
|
32
|
-
return upper_band, rolling_mean, lower_band
|
|
33
|
-
|
|
34
|
-
def macd(price, short_period=12, long_period=26, signal_period=9):
|
|
35
|
-
shortLine = price.ewm(span=short_period, adjust=False).mean()
|
|
36
|
-
longLine = price.ewm(span=long_period, adjust=False).mean()
|
|
37
|
-
macd_line = shortLine - longLine
|
|
38
|
-
signalLine = macd_line.ewm(span=signal_period, adjust=False).mean()
|
|
39
|
-
histogram = macd_line - signalLine
|
|
40
|
-
return macd_line, signalLine, histogram
|
|
41
|
-
|
|
42
|
-
def atr(data, period=14):
|
|
43
|
-
high = data['High']
|
|
44
|
-
low = data['Low']
|
|
45
|
-
close = data['Close']
|
|
46
|
-
tr1 = high - low
|
|
47
|
-
tr2 = abs(high - close.shift())
|
|
48
|
-
tr3 = abs(low - close.shift())
|
|
49
|
-
tr = pd.concat([tr1, tr2, tr3], axis=1).max(axis=1)
|
|
50
|
-
return (tr.rolling(window=period).mean())
|
|
51
|
-
|
|
52
|
-
def sma(price, period=9):
|
|
53
|
-
return price.rolling(window=period).mean()
|
|
54
|
-
|
|
55
|
-
def ema(price, period=9):
|
|
56
|
-
return price.ewm(span=period, adjust=False).mean()
|
|
57
|
-
|
|
58
|
-
def demma(price, period=9):
|
|
59
|
-
ema_val = price.ewm(span=period, adjust=False).mean()
|
|
60
|
-
Eema1 = ema_val.ewm(span=period, adjust=False).mean()
|
|
61
|
-
demma_val = 2 * (ema_val) - (Eema1)
|
|
62
|
-
return demma_val
|
|
63
|
-
|
|
64
|
-
def temma(price, period=9):
|
|
65
|
-
ema_val = price.ewm(span=period, adjust=False).mean()
|
|
66
|
-
Eema1 = ema_val.ewm(span=period, adjust=False).mean()
|
|
67
|
-
Eema2 = Eema1.ewm(span=period, adjust=False).mean()
|
|
68
|
-
temma_val = 3 * ema_val - (3 * Eema1) + Eema2
|
|
69
|
-
return temma_val
|
|
70
|
-
|
|
71
|
-
def RVWAP(high, low, close, volume, period=20):
|
|
72
|
-
tp = (high + low + close) / 3
|
|
73
|
-
tpv = tp * volume
|
|
74
|
-
rolling_tpv = tpv.rolling(window=period).sum()
|
|
75
|
-
rolling_volume = volume.rolling(window=period).sum()
|
|
76
|
-
return rolling_tpv / rolling_volume
|
|
11
|
+
from .indicators import Rsi ,RVWAP , macd , bb_bands , atr ,fetch_data ,sma , temma , demma , ema
|
|
12
|
+
# ============================================================================
|
|
13
|
+
# PERIOD SETTINGS FOR DIFFERENT TIME FRAMES
|
|
14
|
+
# ============================================================================
|
|
15
|
+
PERIOD_SETTINGS = {
|
|
16
|
+
"1M": {"rsi": 14, "bb": 20, "macd": (12, 26, 9), "atr": 14, "ma": 9, "rvwap": 20},
|
|
17
|
+
"3M": {"rsi": 21, "bb": 25, "macd": (12, 26, 9), "atr": 21, "ma": 20, "rvwap": 20},
|
|
18
|
+
"6M": {"rsi": 30, "bb": 30, "macd": (12, 26, 9), "atr": 30, "ma": 30, "rvwap": 30},
|
|
19
|
+
"9M": {"rsi": 40, "bb": 30, "macd": (12, 26, 9), "atr": 30, "ma": 30, "rvwap": 30},
|
|
20
|
+
"1Y": {"rsi": 40, "bb": 30, "macd": (12, 26, 9), "atr": 40, "ma": 50, "rvwap": 30},
|
|
21
|
+
"3Y": {"rsi": 60, "bb": 50, "macd": (26, 52, 18), "atr": 60, "ma": 100, "rvwap": 50},
|
|
22
|
+
"5Y": {"rsi": 70, "bb": 50, "macd": (26, 52, 18), "atr": 70, "ma": 200, "rvwap": 70},
|
|
23
|
+
}
|
|
77
24
|
|
|
78
25
|
|
|
79
26
|
|
|
@@ -81,28 +28,29 @@ class QuantDashboard:
|
|
|
81
28
|
def __init__(self, root):
|
|
82
29
|
self.root = root
|
|
83
30
|
self.root.title("QuantResearch Dashboard")
|
|
84
|
-
|
|
31
|
+
|
|
85
32
|
# Get screen dimensions
|
|
86
33
|
screen_width = self.root.winfo_screenwidth()
|
|
87
34
|
screen_height = self.root.winfo_screenheight()
|
|
88
|
-
|
|
35
|
+
|
|
89
36
|
# Set window size to 90% of screen
|
|
90
37
|
window_width = int(screen_width * 0.9)
|
|
91
38
|
window_height = int(screen_height * 0.85)
|
|
92
|
-
|
|
39
|
+
|
|
93
40
|
# Center the window
|
|
94
41
|
x = (screen_width - window_width) // 2
|
|
95
42
|
y = (screen_height - window_height) // 2
|
|
96
|
-
|
|
97
43
|
self.root.geometry(f"{window_width}x{window_height}+{x}+{y}")
|
|
98
44
|
self.root.configure(bg='#1e1e1e')
|
|
99
|
-
|
|
45
|
+
|
|
100
46
|
# Data storage
|
|
101
47
|
self.data = None
|
|
102
48
|
self.current_ticker = None
|
|
103
49
|
self.current_start = None
|
|
104
50
|
self.current_end = None
|
|
105
|
-
|
|
51
|
+
self.current_timeframe = "1M" # Track selected timeframe
|
|
52
|
+
self.period_config = PERIOD_SETTINGS["1M"] # Store current period config
|
|
53
|
+
|
|
106
54
|
# Indicator states
|
|
107
55
|
self.ma_states = {
|
|
108
56
|
'SMA': tk.BooleanVar(value=False),
|
|
@@ -110,40 +58,40 @@ class QuantDashboard:
|
|
|
110
58
|
'DEMA': tk.BooleanVar(value=False),
|
|
111
59
|
'TEMA': tk.BooleanVar(value=False)
|
|
112
60
|
}
|
|
113
|
-
|
|
61
|
+
|
|
114
62
|
self.indicator_states = {
|
|
115
63
|
'MACD': tk.BooleanVar(value=False),
|
|
116
64
|
'ATR': tk.BooleanVar(value=False),
|
|
117
|
-
'RSI': tk.BooleanVar(value=False)
|
|
65
|
+
'RSI': tk.BooleanVar(value=False),
|
|
66
|
+
'Bollinger Bands': tk.BooleanVar(value=False) # Add Bollinger Bands
|
|
118
67
|
}
|
|
119
|
-
|
|
68
|
+
|
|
120
69
|
self.setup_ui()
|
|
121
|
-
|
|
70
|
+
|
|
122
71
|
def setup_ui(self):
|
|
123
72
|
# Main container
|
|
124
73
|
main_container = tk.Frame(self.root, bg='#1e1e1e')
|
|
125
74
|
main_container.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
|
|
126
|
-
|
|
75
|
+
|
|
127
76
|
# Configure grid weights
|
|
128
77
|
main_container.grid_rowconfigure(0, weight=1)
|
|
129
78
|
main_container.grid_columnconfigure(0, weight=4) # 80% for charts
|
|
130
79
|
main_container.grid_columnconfigure(1, weight=1) # 20% for control panel
|
|
131
|
-
|
|
80
|
+
|
|
132
81
|
# Left side - Charts area (75% width)
|
|
133
82
|
left_container = tk.Frame(main_container, bg='#1e1e1e')
|
|
134
83
|
left_container.grid(row=0, column=0, sticky='nsew', padx=(0, 5))
|
|
135
|
-
|
|
84
|
+
|
|
136
85
|
# Time range selector at the top
|
|
137
86
|
self.time_frame = tk.Frame(left_container, bg='#2d2d2d', height=50, relief=tk.RAISED, borderwidth=2)
|
|
138
87
|
self.time_frame.pack(fill=tk.X, pady=(0, 5))
|
|
139
88
|
self.time_frame.pack_propagate(False)
|
|
140
89
|
self.setup_time_selector()
|
|
141
|
-
|
|
90
|
+
|
|
142
91
|
# Chart display area
|
|
143
92
|
self.chart_container = tk.Frame(left_container, bg='#2d2d2d', relief=tk.RAISED, borderwidth=2)
|
|
144
93
|
self.chart_container.pack(fill=tk.BOTH, expand=True)
|
|
145
94
|
|
|
146
|
-
# --- ADD THIS BLOCK ---
|
|
147
95
|
self.welcome_label = tk.Label(
|
|
148
96
|
self.chart_container,
|
|
149
97
|
text="Welcome to QuantWindow",
|
|
@@ -152,143 +100,143 @@ class QuantDashboard:
|
|
|
152
100
|
fg="#eaff00"
|
|
153
101
|
)
|
|
154
102
|
self.welcome_label.pack(expand=True)
|
|
155
|
-
|
|
103
|
+
|
|
156
104
|
# Right side - PERMANENT Control Panel (25% width)
|
|
157
105
|
self.control_frame = tk.Frame(main_container, bg='#2d2d2d', relief=tk.RAISED, borderwidth=2)
|
|
158
106
|
self.control_frame.grid(row=0, column=1, sticky='nsew')
|
|
159
|
-
|
|
160
107
|
self.setup_control_panel()
|
|
161
|
-
|
|
108
|
+
|
|
162
109
|
def setup_time_selector(self):
|
|
163
110
|
tk.Label(self.time_frame, text="Time Range:", font=('Arial', 9, 'bold'),
|
|
164
111
|
bg='#2d2d2d', fg='#ffffff').pack(side=tk.LEFT, padx=(10, 8))
|
|
165
|
-
|
|
166
|
-
periods = [('5Y', 1825), ('3Y', 1095), ('1Y', 365
|
|
167
|
-
('6M', 180), ('3M', 90), ('1M', 30)]
|
|
168
|
-
|
|
169
|
-
for label, days in periods:
|
|
112
|
+
|
|
113
|
+
periods = [('5Y', 1825, '5Y'), ('3Y', 1095, '3Y'), ('1Y', 365, '1Y'),
|
|
114
|
+
('9M', 270, '9M'), ('6M', 180, '6M'), ('3M', 90, '3M'), ('1M', 30, '1M')]
|
|
115
|
+
|
|
116
|
+
for label, days, timeframe_key in periods:
|
|
170
117
|
btn = tk.Button(self.time_frame, text=label, width=5,
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
118
|
+
command=lambda d=days, tf=timeframe_key: self.change_time_range(d, tf),
|
|
119
|
+
bg='#3d3d3d', fg='#ffffff', font=('Arial', 8, 'bold'),
|
|
120
|
+
relief=tk.RAISED, borderwidth=2, cursor='hand2',
|
|
121
|
+
activebackground='#00ff88', activeforeground='#1e1e1e')
|
|
175
122
|
btn.pack(side=tk.LEFT, padx=2)
|
|
176
|
-
|
|
123
|
+
|
|
177
124
|
def setup_control_panel(self):
|
|
178
125
|
# Main frame with fixed structure
|
|
179
126
|
control_main = tk.Frame(self.control_frame, bg='#2d2d2d')
|
|
180
127
|
control_main.pack(fill=tk.BOTH, expand=True)
|
|
181
|
-
|
|
128
|
+
|
|
182
129
|
# Title (fixed at top)
|
|
183
130
|
title_label = tk.Label(control_main, text="Control Panel", font=('Arial', 14, 'bold'),
|
|
184
|
-
|
|
131
|
+
bg='#2d2d2d', fg='#00ff88')
|
|
185
132
|
title_label.pack(pady=(10, 15), anchor='n')
|
|
186
|
-
|
|
133
|
+
|
|
187
134
|
# Scrollable content area
|
|
188
135
|
canvas = tk.Canvas(control_main, bg='#2d2d2d', highlightthickness=0)
|
|
189
136
|
scrollbar = tk.Scrollbar(control_main, orient="vertical", command=canvas.yview)
|
|
190
137
|
scrollable_frame = tk.Frame(canvas, bg='#2d2d2d')
|
|
191
|
-
|
|
138
|
+
|
|
192
139
|
def on_frame_configure(event=None):
|
|
193
140
|
canvas.configure(scrollregion=canvas.bbox("all"))
|
|
194
|
-
|
|
141
|
+
|
|
195
142
|
def on_canvas_configure(event):
|
|
196
|
-
# Update the scrollable frame width to match canvas width
|
|
197
143
|
canvas.itemconfig(canvas_window, width=event.width)
|
|
198
|
-
|
|
144
|
+
|
|
199
145
|
scrollable_frame.bind("<Configure>", on_frame_configure)
|
|
200
146
|
canvas.bind("<Configure>", on_canvas_configure)
|
|
201
|
-
|
|
147
|
+
|
|
202
148
|
canvas_window = canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
|
|
203
149
|
canvas.configure(yscrollcommand=scrollbar.set)
|
|
204
|
-
|
|
150
|
+
|
|
205
151
|
# Mouse wheel scrolling
|
|
206
152
|
def _on_mousewheel(event):
|
|
207
153
|
canvas.yview_scroll(int(-1*(event.delta/120)), "units")
|
|
208
|
-
|
|
154
|
+
|
|
209
155
|
canvas.bind_all("<MouseWheel>", _on_mousewheel)
|
|
210
|
-
|
|
156
|
+
|
|
211
157
|
# Ticker input
|
|
212
158
|
ticker_frame = tk.Frame(scrollable_frame, bg='#2d2d2d')
|
|
213
159
|
ticker_frame.pack(pady=8, padx=15, fill=tk.X)
|
|
214
|
-
|
|
160
|
+
|
|
215
161
|
tk.Label(ticker_frame, text="Company Ticker:", font=('Arial', 9, 'bold'),
|
|
216
162
|
bg='#2d2d2d', fg='#ffffff').pack(anchor=tk.W)
|
|
217
|
-
|
|
163
|
+
|
|
218
164
|
self.ticker_var = tk.StringVar()
|
|
219
165
|
ticker_entry = tk.Entry(ticker_frame, textvariable=self.ticker_var, font=('Arial', 10),
|
|
220
|
-
|
|
221
|
-
|
|
166
|
+
bg='#3d3d3d', fg='#ffffff', insertbackground='#ffffff',
|
|
167
|
+
relief=tk.SOLID, borderwidth=1)
|
|
222
168
|
ticker_entry.pack(fill=tk.X, pady=(5, 0), ipady=4)
|
|
223
169
|
ticker_entry.bind('<Return>', lambda e: self.fetch_and_plot())
|
|
224
|
-
|
|
170
|
+
|
|
225
171
|
# Date inputs
|
|
226
172
|
date_frame = tk.Frame(scrollable_frame, bg='#2d2d2d')
|
|
227
173
|
date_frame.pack(pady=8, padx=15, fill=tk.X)
|
|
228
|
-
|
|
174
|
+
|
|
229
175
|
tk.Label(date_frame, text="Start Date:", font=('Arial', 9, 'bold'),
|
|
230
176
|
bg='#2d2d2d', fg='#ffffff').pack(anchor=tk.W)
|
|
177
|
+
|
|
231
178
|
self.start_date = DateEntry(date_frame, width=23, background='#3d3d3d',
|
|
232
|
-
|
|
179
|
+
foreground='white', borderwidth=2, date_pattern='yyyy-mm-dd')
|
|
233
180
|
self.start_date.pack(fill=tk.X, pady=(5, 8))
|
|
234
|
-
self.start_date.set_date(datetime.now() - timedelta(days=
|
|
235
|
-
|
|
181
|
+
self.start_date.set_date(datetime.now() - timedelta(days=30))
|
|
182
|
+
|
|
236
183
|
tk.Label(date_frame, text="End Date:", font=('Arial', 9, 'bold'),
|
|
237
184
|
bg='#2d2d2d', fg='#ffffff').pack(anchor=tk.W)
|
|
185
|
+
|
|
238
186
|
self.end_date = DateEntry(date_frame, width=23, background='#3d3d3d',
|
|
239
|
-
|
|
187
|
+
foreground='white', borderwidth=2, date_pattern='yyyy-mm-dd')
|
|
240
188
|
self.end_date.pack(fill=tk.X, pady=(5, 0))
|
|
241
|
-
|
|
189
|
+
|
|
242
190
|
# Fetch button
|
|
243
191
|
fetch_btn = tk.Button(scrollable_frame, text="📊 Fetch Data", command=self.fetch_and_plot,
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
192
|
+
bg='#00ff88', fg='#1e1e1e', font=('Arial', 10, 'bold'),
|
|
193
|
+
relief=tk.RAISED, borderwidth=3, cursor='hand2',
|
|
194
|
+
activebackground='#00dd77')
|
|
247
195
|
fetch_btn.pack(pady=12, padx=15, fill=tk.X, ipady=5)
|
|
248
|
-
|
|
196
|
+
|
|
249
197
|
# Separator
|
|
250
198
|
tk.Frame(scrollable_frame, height=2, bg='#00ff88').pack(fill=tk.X, padx=15, pady=10)
|
|
251
|
-
|
|
199
|
+
|
|
252
200
|
# Moving Averages section
|
|
253
201
|
ma_label = tk.Label(scrollable_frame, text="📈 Moving Averages", font=('Arial', 11, 'bold'),
|
|
254
|
-
|
|
202
|
+
bg='#2d2d2d', fg='#00ff88')
|
|
255
203
|
ma_label.pack(pady=(8, 8))
|
|
256
|
-
|
|
204
|
+
|
|
257
205
|
ma_frame = tk.Frame(scrollable_frame, bg='#2d2d2d')
|
|
258
206
|
ma_frame.pack(pady=5, padx=15, fill=tk.X)
|
|
259
|
-
|
|
207
|
+
|
|
260
208
|
for ma_name in ['SMA', 'EMA', 'DEMA', 'TEMA']:
|
|
261
209
|
self.create_toggle_button(ma_frame, ma_name, self.ma_states[ma_name], is_ma=True)
|
|
262
|
-
|
|
210
|
+
|
|
263
211
|
# Separator
|
|
264
212
|
tk.Frame(scrollable_frame, height=2, bg='#00ff88').pack(fill=tk.X, padx=15, pady=10)
|
|
265
|
-
|
|
213
|
+
|
|
266
214
|
# Technical Indicators section
|
|
267
215
|
ind_label = tk.Label(scrollable_frame, text="📊 Technical Indicators", font=('Arial', 11, 'bold'),
|
|
268
|
-
|
|
216
|
+
bg='#2d2d2d', fg='#00ff88')
|
|
269
217
|
ind_label.pack(pady=(8, 8))
|
|
270
|
-
|
|
218
|
+
|
|
271
219
|
ind_frame = tk.Frame(scrollable_frame, bg='#2d2d2d')
|
|
272
220
|
ind_frame.pack(pady=5, padx=15, fill=tk.X)
|
|
273
|
-
|
|
274
|
-
for ind_name in ['MACD', 'ATR', '
|
|
221
|
+
|
|
222
|
+
for ind_name in ['RSI', 'MACD', 'ATR', 'Bollinger Bands']:
|
|
275
223
|
self.create_toggle_button(ind_frame, ind_name, self.indicator_states[ind_name], is_ma=False)
|
|
276
|
-
|
|
224
|
+
|
|
277
225
|
# Add some bottom padding
|
|
278
226
|
tk.Frame(scrollable_frame, height=20, bg='#2d2d2d').pack()
|
|
279
|
-
|
|
227
|
+
|
|
280
228
|
# Pack canvas and scrollbar
|
|
281
229
|
canvas.pack(side="left", fill="both", expand=True)
|
|
282
230
|
scrollbar.pack(side="right", fill="y")
|
|
283
|
-
|
|
231
|
+
|
|
284
232
|
def create_toggle_button(self, parent, name, var, is_ma=True):
|
|
285
233
|
frame = tk.Frame(parent, bg='#2d2d2d')
|
|
286
234
|
frame.pack(pady=6, fill=tk.X)
|
|
287
|
-
|
|
235
|
+
|
|
288
236
|
label = tk.Label(frame, text=name, font=('Arial', 9, 'bold'),
|
|
289
|
-
bg='#2d2d2d', fg='#ffffff', width=
|
|
237
|
+
bg='#2d2d2d', fg='#ffffff', width=12, anchor=tk.W)
|
|
290
238
|
label.pack(side=tk.LEFT, padx=(0, 8))
|
|
291
|
-
|
|
239
|
+
|
|
292
240
|
btn = tk.Button(frame, text="OFF", width=8,
|
|
293
241
|
command=lambda: self.toggle_indicator(name, var, btn, is_ma),
|
|
294
242
|
bg='#ff4444', fg='white', font=('Arial', 8, 'bold'),
|
|
@@ -296,284 +244,329 @@ class QuantDashboard:
|
|
|
296
244
|
activebackground='#ff6666')
|
|
297
245
|
btn.pack(side=tk.RIGHT)
|
|
298
246
|
btn.var = var
|
|
299
|
-
|
|
247
|
+
|
|
300
248
|
def toggle_indicator(self, name, var, btn, is_ma):
|
|
301
249
|
var.set(not var.get())
|
|
302
250
|
if var.get():
|
|
303
251
|
btn.config(text="ON", bg='#00ff88', fg='#1e1e1e', activebackground='#00dd77')
|
|
304
252
|
else:
|
|
305
253
|
btn.config(text="OFF", bg='#ff4444', fg='white', activebackground='#ff6666')
|
|
306
|
-
|
|
254
|
+
|
|
307
255
|
if self.data is not None:
|
|
308
256
|
self.update_plot()
|
|
309
|
-
|
|
310
|
-
def change_time_range(self, days):
|
|
257
|
+
|
|
258
|
+
def change_time_range(self, days, timeframe_key):
|
|
311
259
|
end_date = datetime.now()
|
|
312
260
|
start_date = end_date - timedelta(days=days)
|
|
313
|
-
|
|
314
261
|
self.start_date.set_date(start_date)
|
|
315
262
|
self.end_date.set_date(end_date)
|
|
316
263
|
|
|
264
|
+
# Update timeframe and period config
|
|
265
|
+
self.current_timeframe = timeframe_key
|
|
266
|
+
self.period_config = PERIOD_SETTINGS.get(timeframe_key, PERIOD_SETTINGS["1M"])
|
|
267
|
+
|
|
317
268
|
if self.current_ticker:
|
|
318
269
|
self.fetch_and_plot()
|
|
319
|
-
|
|
270
|
+
|
|
320
271
|
def fetch_and_plot(self):
|
|
321
272
|
ticker = self.ticker_var.get().strip().upper()
|
|
322
273
|
if not ticker:
|
|
323
274
|
messagebox.showwarning("Input Error", "Please enter a ticker symbol")
|
|
324
275
|
return
|
|
325
|
-
|
|
276
|
+
|
|
326
277
|
start = self.start_date.get_date().strftime('%Y-%m-%d')
|
|
327
278
|
end = self.end_date.get_date().strftime('%Y-%m-%d')
|
|
328
|
-
|
|
279
|
+
|
|
329
280
|
try:
|
|
330
281
|
print(f"Fetching data for {ticker} from {start} to {end}...")
|
|
331
|
-
|
|
332
|
-
# Show loading message
|
|
282
|
+
|
|
333
283
|
# Remove welcome screen on first action
|
|
334
284
|
for widget in self.chart_container.winfo_children():
|
|
335
285
|
widget.destroy()
|
|
336
286
|
|
|
337
|
-
# Ensure welcome label is gone
|
|
338
287
|
self.welcome_label = None
|
|
339
|
-
|
|
288
|
+
|
|
340
289
|
loading_label = tk.Label(self.chart_container, text=f"Loading {ticker} data...",
|
|
341
290
|
font=('Arial', 16, 'bold'), bg='#2d2d2d', fg='#00ff88')
|
|
342
291
|
loading_label.pack(expand=True)
|
|
292
|
+
|
|
343
293
|
self.root.update()
|
|
344
|
-
|
|
294
|
+
|
|
345
295
|
# Fetch data
|
|
346
296
|
self.data = fetch_data(ticker, start, end)
|
|
347
|
-
|
|
297
|
+
|
|
348
298
|
if self.data is None or self.data.empty:
|
|
349
299
|
messagebox.showerror("Data Error", f"No data found for {ticker}")
|
|
350
300
|
loading_label.destroy()
|
|
351
301
|
return
|
|
352
|
-
|
|
302
|
+
|
|
353
303
|
self.current_ticker = ticker
|
|
354
304
|
self.current_start = start
|
|
355
305
|
self.current_end = end
|
|
356
|
-
|
|
306
|
+
|
|
357
307
|
print(f"Data fetched successfully! Shape: {self.data.shape}")
|
|
358
308
|
print(f"Columns: {self.data.columns.tolist()}")
|
|
359
309
|
print(f"Date range: {self.data.index[0]} to {self.data.index[-1]}")
|
|
360
|
-
|
|
310
|
+
print(f"Current Period Config: {self.period_config}")
|
|
311
|
+
|
|
361
312
|
self.update_plot()
|
|
362
|
-
|
|
313
|
+
|
|
363
314
|
except Exception as e:
|
|
364
315
|
messagebox.showerror("Error", f"Error fetching data: {str(e)}")
|
|
365
316
|
print(f"Error details: {e}")
|
|
366
317
|
import traceback
|
|
367
318
|
traceback.print_exc()
|
|
368
|
-
|
|
319
|
+
|
|
369
320
|
def update_plot(self):
|
|
370
321
|
if self.data is None or self.data.empty:
|
|
371
322
|
return
|
|
372
|
-
|
|
323
|
+
|
|
373
324
|
# Clear existing charts
|
|
374
325
|
for widget in self.chart_container.winfo_children():
|
|
375
326
|
widget.destroy()
|
|
376
|
-
|
|
327
|
+
|
|
377
328
|
# Count active bottom indicators
|
|
378
|
-
active_indicators = [name for name, var in self.indicator_states.items()
|
|
329
|
+
active_indicators = [name for name, var in self.indicator_states.items()
|
|
330
|
+
if var.get() and name != 'Bollinger Bands']
|
|
331
|
+
|
|
379
332
|
num_indicators = len(active_indicators)
|
|
380
|
-
|
|
333
|
+
|
|
381
334
|
# Create figure with dynamic subplot layout
|
|
382
335
|
if num_indicators == 0:
|
|
383
336
|
fig = Figure(figsize=(12, 7), facecolor='#2d2d2d', dpi=100)
|
|
384
337
|
gs = fig.add_gridspec(1, 1)
|
|
385
338
|
axes = [fig.add_subplot(gs[0, 0])]
|
|
339
|
+
|
|
386
340
|
elif num_indicators == 1:
|
|
387
341
|
fig = Figure(figsize=(12, 7), facecolor='#2d2d2d', dpi=100)
|
|
388
342
|
gs = fig.add_gridspec(2, 1, height_ratios=[3, 1], hspace=0.3)
|
|
389
343
|
axes = [fig.add_subplot(gs[0, 0]), fig.add_subplot(gs[1, 0])]
|
|
344
|
+
|
|
390
345
|
elif num_indicators == 2:
|
|
391
346
|
fig = Figure(figsize=(12, 7), facecolor='#2d2d2d', dpi=100)
|
|
392
347
|
gs = fig.add_gridspec(2, 2, height_ratios=[3, 1], hspace=0.3, wspace=0.3)
|
|
393
348
|
axes = [fig.add_subplot(gs[0, :]), fig.add_subplot(gs[1, 0]), fig.add_subplot(gs[1, 1])]
|
|
394
|
-
|
|
349
|
+
|
|
350
|
+
else: # 3 or more indicators
|
|
395
351
|
fig = Figure(figsize=(12, 7), facecolor='#2d2d2d', dpi=100)
|
|
396
352
|
gs = fig.add_gridspec(2, 3, height_ratios=[3, 1], hspace=0.3, wspace=0.3)
|
|
397
|
-
axes = [fig.add_subplot(gs[0, :]), fig.add_subplot(gs[1, 0]),
|
|
353
|
+
axes = [fig.add_subplot(gs[0, :]), fig.add_subplot(gs[1, 0]),
|
|
398
354
|
fig.add_subplot(gs[1, 1]), fig.add_subplot(gs[1, 2])]
|
|
399
|
-
|
|
355
|
+
|
|
400
356
|
# Main price chart with candlesticks
|
|
401
357
|
ax_main = axes[0]
|
|
358
|
+
|
|
402
359
|
self.plot_candlesticks(ax_main)
|
|
403
|
-
|
|
360
|
+
|
|
361
|
+
# Add Bollinger Bands to main chart if enabled
|
|
362
|
+
if self.indicator_states['Bollinger Bands'].get():
|
|
363
|
+
self.add_bollinger_bands(ax_main)
|
|
364
|
+
|
|
404
365
|
# Add moving averages to main chart
|
|
405
366
|
self.add_moving_averages(ax_main)
|
|
406
|
-
|
|
407
|
-
ax_main.set_title(f'{self.current_ticker} - Price Chart',
|
|
408
|
-
|
|
367
|
+
|
|
368
|
+
ax_main.set_title(f'{self.current_ticker} - Price Chart | Period Config: {self.period_config}',
|
|
369
|
+
color='#ffffff', fontsize=12, fontweight='bold', pad=10)
|
|
409
370
|
ax_main.set_ylabel('Price ($)', color='#ffffff', fontsize=9)
|
|
410
371
|
ax_main.tick_params(colors='#ffffff', labelsize=7)
|
|
411
372
|
ax_main.grid(True, alpha=0.2, color='#ffffff', linestyle='--')
|
|
412
|
-
|
|
373
|
+
|
|
413
374
|
# Only show legend if there are items to display
|
|
414
375
|
handles, labels = ax_main.get_legend_handles_labels()
|
|
415
376
|
if handles:
|
|
416
|
-
ax_main.legend(loc='upper left', facecolor='#2d2d2d',
|
|
417
|
-
|
|
418
|
-
|
|
377
|
+
ax_main.legend(loc='upper left', facecolor='#2d2d2d',
|
|
378
|
+
edgecolor='#00ff88', labelcolor='#ffffff', fontsize=7)
|
|
419
379
|
ax_main.set_facecolor('#1e1e1e')
|
|
420
|
-
|
|
380
|
+
|
|
421
381
|
# Plot active indicators
|
|
422
382
|
for idx, ind_name in enumerate(active_indicators):
|
|
423
383
|
ax_ind = axes[idx + 1]
|
|
424
384
|
ax_ind.set_facecolor('#1e1e1e')
|
|
425
|
-
|
|
385
|
+
|
|
426
386
|
if ind_name == 'RSI':
|
|
427
387
|
self.plot_rsi_indicator(ax_ind)
|
|
428
388
|
elif ind_name == 'MACD':
|
|
429
389
|
self.plot_macd_indicator(ax_ind)
|
|
430
390
|
elif ind_name == 'ATR':
|
|
431
391
|
self.plot_atr_indicator(ax_ind)
|
|
432
|
-
|
|
392
|
+
|
|
433
393
|
fig.tight_layout(pad=1.5)
|
|
434
|
-
|
|
394
|
+
|
|
435
395
|
# Embed figure
|
|
436
396
|
canvas = FigureCanvasTkAgg(fig, self.chart_container)
|
|
437
397
|
canvas.draw()
|
|
438
398
|
canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)
|
|
439
|
-
|
|
399
|
+
|
|
440
400
|
def plot_candlesticks(self, ax):
|
|
441
401
|
"""Plot candlestick chart"""
|
|
442
402
|
data_clean = self.data[['Open', 'High', 'Low', 'Close']].dropna()
|
|
443
|
-
|
|
403
|
+
|
|
444
404
|
if len(data_clean) == 0:
|
|
445
405
|
return
|
|
446
|
-
|
|
406
|
+
|
|
447
407
|
open_prices = data_clean['Open'].values
|
|
448
408
|
close_prices = data_clean['Close'].values
|
|
449
409
|
high_prices = data_clean['High'].values
|
|
450
410
|
low_prices = data_clean['Low'].values
|
|
451
|
-
|
|
411
|
+
|
|
452
412
|
x = range(len(open_prices))
|
|
453
413
|
width = 0.6
|
|
454
|
-
|
|
414
|
+
|
|
455
415
|
for i in x:
|
|
456
|
-
color = "#00ff00" if close_prices[i] >= open_prices[i] else "#
|
|
457
|
-
|
|
416
|
+
color = "#00ff00" if close_prices[i] >= open_prices[i] else "#ff0000"
|
|
417
|
+
|
|
458
418
|
# Draw wick
|
|
459
|
-
ax.plot([i, i], [low_prices[i], high_prices[i]],
|
|
419
|
+
ax.plot([i, i], [low_prices[i], high_prices[i]],
|
|
460
420
|
color='#ffffff', linewidth=0.8, alpha=0.8)
|
|
461
|
-
|
|
421
|
+
|
|
462
422
|
# Draw body
|
|
463
423
|
body_height = abs(close_prices[i] - open_prices[i])
|
|
464
424
|
body_bottom = min(open_prices[i], close_prices[i])
|
|
465
|
-
|
|
425
|
+
|
|
466
426
|
rect = plt.Rectangle((i - width/2, body_bottom), width, body_height,
|
|
467
|
-
|
|
468
|
-
|
|
427
|
+
facecolor=color, edgecolor='#ffffff',
|
|
428
|
+
linewidth=0.5, alpha=0.9)
|
|
469
429
|
ax.add_patch(rect)
|
|
470
|
-
|
|
430
|
+
|
|
471
431
|
# Format x-axis with dates
|
|
472
432
|
step = max(1, len(data_clean) // 10)
|
|
473
433
|
tick_positions = list(range(0, len(data_clean), step))
|
|
474
434
|
tick_labels = [data_clean.index[i].strftime('%Y-%m-%d') for i in tick_positions]
|
|
435
|
+
|
|
475
436
|
ax.set_xticks(tick_positions)
|
|
476
437
|
ax.set_xticklabels(tick_labels, rotation=45, ha='right')
|
|
477
|
-
|
|
438
|
+
|
|
439
|
+
def add_bollinger_bands(self, ax):
|
|
440
|
+
"""Add Bollinger Bands to the price chart"""
|
|
441
|
+
bb_period = self.period_config.get('bb', 20)
|
|
442
|
+
bb_upper, bb_mid, bb_lower = bb_bands(self.data['Close'], period=bb_period)
|
|
443
|
+
|
|
444
|
+
x = range(len(self.data))
|
|
445
|
+
|
|
446
|
+
ax.plot(x, bb_upper.values, color='#FFD700', label=f'BB Upper ({bb_period})',
|
|
447
|
+
linestyle='--', linewidth=1.5, alpha=0.7)
|
|
448
|
+
ax.plot(x, bb_mid.values, color='#00FFFF', label=f'BB Middle ({bb_period})',
|
|
449
|
+
linestyle='-.', linewidth=1, alpha=0.6)
|
|
450
|
+
ax.plot(x, bb_lower.values, color='#FFD700', label=f'BB Lower ({bb_period})',
|
|
451
|
+
linestyle='--', linewidth=1.5, alpha=0.7)
|
|
452
|
+
|
|
453
|
+
# Fill between upper and lower bands
|
|
454
|
+
ax.fill_between(x, bb_upper.values, bb_lower.values, alpha=0.1, color='#FFD700')
|
|
455
|
+
|
|
478
456
|
def add_moving_averages(self, ax):
|
|
479
457
|
"""Add moving averages to the chart"""
|
|
480
458
|
x = range(len(self.data))
|
|
481
|
-
|
|
459
|
+
ma_period = self.period_config.get('ma', 9)
|
|
460
|
+
|
|
482
461
|
if self.ma_states['SMA'].get():
|
|
483
|
-
sma_val = sma(self.data['Close'], period=
|
|
484
|
-
ax.plot(x, sma_val.values, label='SMA(
|
|
462
|
+
sma_val = sma(self.data['Close'], period=ma_period)
|
|
463
|
+
ax.plot(x, sma_val.values, label=f'SMA({ma_period})',
|
|
485
464
|
color='#00aaff', linewidth=1.5, linestyle='--', alpha=0.8)
|
|
486
|
-
|
|
465
|
+
|
|
487
466
|
if self.ma_states['EMA'].get():
|
|
488
|
-
ema_val = ema(self.data['Close'], period=
|
|
489
|
-
ax.plot(x, ema_val.values, label='EMA(
|
|
467
|
+
ema_val = ema(self.data['Close'], period=ma_period)
|
|
468
|
+
ax.plot(x, ema_val.values, label=f'EMA({ma_period})',
|
|
490
469
|
color='#ff9500', linewidth=1.5, linestyle='--', alpha=0.8)
|
|
491
|
-
|
|
470
|
+
|
|
492
471
|
if self.ma_states['DEMA'].get():
|
|
493
|
-
dema_val = demma(self.data['Close'], period=
|
|
494
|
-
ax.plot(x, dema_val.values, label='DEMA(
|
|
472
|
+
dema_val = demma(self.data['Close'], period=ma_period)
|
|
473
|
+
ax.plot(x, dema_val.values, label=f'DEMA({ma_period})',
|
|
495
474
|
color='#af52de', linewidth=1.5, linestyle='-.', alpha=0.8)
|
|
496
|
-
|
|
475
|
+
|
|
497
476
|
if self.ma_states['TEMA'].get():
|
|
498
|
-
tema_val = temma(self.data['Close'], period=
|
|
499
|
-
ax.plot(x, tema_val.values, label='TEMA(
|
|
477
|
+
tema_val = temma(self.data['Close'], period=ma_period)
|
|
478
|
+
ax.plot(x, tema_val.values, label=f'TEMA({ma_period})',
|
|
500
479
|
color='#ffcc00', linewidth=1.5, linestyle='-.', alpha=0.8)
|
|
501
|
-
|
|
480
|
+
|
|
502
481
|
def plot_rsi_indicator(self, ax):
|
|
503
482
|
"""Plot RSI indicator"""
|
|
504
|
-
|
|
483
|
+
rsi_period = self.period_config.get('rsi', 14)
|
|
484
|
+
rsi_val = Rsi(self.data['Close'], period=rsi_period)
|
|
485
|
+
|
|
505
486
|
x = range(len(rsi_val))
|
|
506
|
-
|
|
487
|
+
|
|
507
488
|
ax.plot(x, rsi_val.values, color='#af52de', linewidth=2)
|
|
508
489
|
ax.axhline(y=70, color='#ff4444', linestyle='--', linewidth=1.5, alpha=0.7)
|
|
509
490
|
ax.axhline(y=30, color='#00ff88', linestyle='--', linewidth=1.5, alpha=0.7)
|
|
491
|
+
|
|
510
492
|
ax.fill_between(x, 70, 100, alpha=0.1, color='#ff4444')
|
|
511
493
|
ax.fill_between(x, 0, 30, alpha=0.1, color='#00ff88')
|
|
512
|
-
|
|
494
|
+
|
|
513
495
|
ax.set_ylim(0, 100)
|
|
514
|
-
ax.set_title('RSI (
|
|
496
|
+
ax.set_title(f'RSI ({rsi_period})', color='#ffffff', fontsize=10, fontweight='bold')
|
|
515
497
|
ax.set_ylabel('RSI', color='#ffffff', fontsize=8)
|
|
516
498
|
ax.tick_params(colors='#ffffff', labelsize=6)
|
|
517
499
|
ax.grid(True, alpha=0.2, color='#ffffff', linestyle='--')
|
|
518
|
-
|
|
500
|
+
|
|
519
501
|
# Format x-axis
|
|
520
502
|
step = max(1, len(self.data) // 5)
|
|
521
503
|
tick_positions = list(range(0, len(self.data), step))
|
|
522
504
|
tick_labels = [self.data.index[i].strftime('%m-%d') for i in tick_positions]
|
|
505
|
+
|
|
523
506
|
ax.set_xticks(tick_positions)
|
|
524
507
|
ax.set_xticklabels(tick_labels, rotation=45, ha='right')
|
|
525
|
-
|
|
508
|
+
|
|
526
509
|
def plot_macd_indicator(self, ax):
|
|
527
510
|
"""Plot MACD indicator"""
|
|
528
|
-
|
|
511
|
+
macd_config = self.period_config.get('macd', (12, 26, 9))
|
|
512
|
+
short_period, long_period, signal_period = macd_config
|
|
513
|
+
|
|
514
|
+
macd_line, signal_line, hist = macd(self.data['Close'],
|
|
515
|
+
short_period=short_period,
|
|
516
|
+
long_period=long_period,
|
|
517
|
+
signal_period=signal_period)
|
|
518
|
+
|
|
529
519
|
x = range(len(macd_line))
|
|
530
|
-
|
|
520
|
+
|
|
531
521
|
ax.plot(x, macd_line.values, label='MACD', color='#00aaff', linewidth=1.5)
|
|
532
522
|
ax.plot(x, signal_line.values, label='Signal', color='#ff9500', linewidth=1.5)
|
|
533
|
-
|
|
523
|
+
|
|
534
524
|
colors = ['#00ff88' if val >= 0 else '#ff4444' for val in hist.values]
|
|
535
525
|
ax.bar(x, hist.values, color=colors, alpha=0.6, width=0.8)
|
|
536
|
-
|
|
526
|
+
|
|
537
527
|
ax.axhline(y=0, color='#ffffff', linestyle='-', linewidth=0.8, alpha=0.5)
|
|
538
|
-
|
|
528
|
+
|
|
529
|
+
ax.set_title(f'MACD ({short_period}/{long_period}/{signal_period})',
|
|
530
|
+
color='#ffffff', fontsize=10, fontweight='bold')
|
|
539
531
|
ax.set_ylabel('Value', color='#ffffff', fontsize=8)
|
|
540
532
|
ax.tick_params(colors='#ffffff', labelsize=6)
|
|
541
533
|
ax.grid(True, alpha=0.2, color='#ffffff', linestyle='--')
|
|
542
534
|
ax.legend(loc='upper left', fontsize=6, facecolor='#2d2d2d',
|
|
543
535
|
edgecolor='#00ff88', labelcolor='#ffffff')
|
|
544
|
-
|
|
536
|
+
|
|
545
537
|
# Format x-axis
|
|
546
538
|
step = max(1, len(self.data) // 5)
|
|
547
539
|
tick_positions = list(range(0, len(self.data), step))
|
|
548
540
|
tick_labels = [self.data.index[i].strftime('%m-%d') for i in tick_positions]
|
|
541
|
+
|
|
549
542
|
ax.set_xticks(tick_positions)
|
|
550
543
|
ax.set_xticklabels(tick_labels, rotation=45, ha='right')
|
|
551
|
-
|
|
544
|
+
|
|
552
545
|
def plot_atr_indicator(self, ax):
|
|
553
546
|
"""Plot ATR indicator"""
|
|
554
|
-
|
|
547
|
+
atr_period = self.period_config.get('atr', 14)
|
|
548
|
+
atr_val = atr(self.data, period=atr_period)
|
|
549
|
+
|
|
555
550
|
x = range(len(atr_val))
|
|
556
|
-
|
|
551
|
+
|
|
557
552
|
ax.plot(x, atr_val.values, color='#ff9500', linewidth=2)
|
|
558
553
|
ax.fill_between(x, 0, atr_val.values, alpha=0.2, color='#ff9500')
|
|
559
|
-
|
|
560
|
-
ax.set_title('ATR (
|
|
554
|
+
|
|
555
|
+
ax.set_title(f'ATR ({atr_period})', color='#ffffff', fontsize=10, fontweight='bold')
|
|
561
556
|
ax.set_ylabel('ATR', color='#ffffff', fontsize=8)
|
|
562
557
|
ax.tick_params(colors='#ffffff', labelsize=6)
|
|
563
558
|
ax.grid(True, alpha=0.2, color='#ffffff', linestyle='--')
|
|
564
|
-
|
|
559
|
+
|
|
565
560
|
# Format x-axis
|
|
566
561
|
step = max(1, len(self.data) // 5)
|
|
567
562
|
tick_positions = list(range(0, len(self.data), step))
|
|
568
563
|
tick_labels = [self.data.index[i].strftime('%m-%d') for i in tick_positions]
|
|
564
|
+
|
|
569
565
|
ax.set_xticks(tick_positions)
|
|
570
566
|
ax.set_xticklabels(tick_labels, rotation=45, ha='right')
|
|
571
567
|
|
|
572
|
-
|
|
573
568
|
def launch_dashboard():
|
|
574
569
|
"""Main function to launch the QuantResearch Dashboard"""
|
|
575
570
|
root = tk.Tk()
|
|
576
571
|
app = QuantDashboard(root)
|
|
577
572
|
root.mainloop()
|
|
578
|
-
|
|
579
|
-
launch_dashboard()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: QuantResearch
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3
|
|
4
4
|
Summary: Technical indicators and visualization tools for quantitative research
|
|
5
5
|
Home-page: https://github.com/vinayak1729-web/QuantR
|
|
6
6
|
Author: Vinayak ShindeVishal Mishra
|
|
@@ -9,7 +9,7 @@ License: MIT
|
|
|
9
9
|
Project-URL: Homepage, https://github.com/vinayak1729-web/QuantR
|
|
10
10
|
Project-URL: Repository, https://github.com/vinayak1729-web/QuantR
|
|
11
11
|
Project-URL: Issues, https://github.com/vinayak1729-web/QuantR/issues
|
|
12
|
-
Project-URL: Documentation, https://
|
|
12
|
+
Project-URL: Documentation, https://vinayak1729-web.github.io/QuantR/
|
|
13
13
|
Keywords: finance,trading,technical-analysis,indicators,quantitative,stocks,yfinance
|
|
14
14
|
Classifier: Development Status :: 3 - Alpha
|
|
15
15
|
Classifier: Intended Audience :: Developers
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "QuantResearch"
|
|
7
|
-
version = "2.
|
|
7
|
+
version = "2.3"
|
|
8
8
|
description = "Technical indicators and visualization tools for quantitative research"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.7"
|
|
@@ -57,7 +57,7 @@ dev = [
|
|
|
57
57
|
Homepage = "https://github.com/vinayak1729-web/QuantR"
|
|
58
58
|
Repository = "https://github.com/vinayak1729-web/QuantR"
|
|
59
59
|
Issues = "https://github.com/vinayak1729-web/QuantR/issues"
|
|
60
|
-
Documentation = "https://
|
|
60
|
+
Documentation = "https://vinayak1729-web.github.io/QuantR/"
|
|
61
61
|
|
|
62
62
|
[tool.setuptools.packages.find]
|
|
63
63
|
where = ["."]
|
|
@@ -5,7 +5,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
|
|
|
5
5
|
|
|
6
6
|
setup(
|
|
7
7
|
name="QuantResearch",
|
|
8
|
-
version="
|
|
8
|
+
version="2.3",
|
|
9
9
|
author="Vinayak Shinde" "Vishal Mishra",
|
|
10
10
|
author_email="vinayak.r.shinde.1729@gmail.com",
|
|
11
11
|
description="Technical indicators and visualization tools for quantitative research",
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
from .indicators import (
|
|
2
|
-
fetch_data,
|
|
3
|
-
Rsi,
|
|
4
|
-
bb_bands,
|
|
5
|
-
macd,
|
|
6
|
-
atr,
|
|
7
|
-
sma,
|
|
8
|
-
ema,
|
|
9
|
-
demma,
|
|
10
|
-
temma,
|
|
11
|
-
RVWAP
|
|
12
|
-
)
|
|
13
|
-
|
|
14
|
-
from .visualize import (
|
|
15
|
-
plot_candlestick,
|
|
16
|
-
plot_macd,
|
|
17
|
-
plot_bollinger,
|
|
18
|
-
plot_rsi,
|
|
19
|
-
plot_moving_averages
|
|
20
|
-
)
|
|
21
|
-
|
|
22
|
-
# Import dashboard functionality
|
|
23
|
-
try:
|
|
24
|
-
from .dashboard import launch_dashboard
|
|
25
|
-
__all__ = [
|
|
26
|
-
# Data fetching
|
|
27
|
-
'fetch_data',
|
|
28
|
-
|
|
29
|
-
# Indicators
|
|
30
|
-
'Rsi',
|
|
31
|
-
'bb_bands',
|
|
32
|
-
'macd',
|
|
33
|
-
'atr',
|
|
34
|
-
'sma',
|
|
35
|
-
'ema',
|
|
36
|
-
'demma',
|
|
37
|
-
'temma',
|
|
38
|
-
'RVWAP',
|
|
39
|
-
|
|
40
|
-
# Visualization
|
|
41
|
-
'plot_candlestick',
|
|
42
|
-
'plot_macd',
|
|
43
|
-
'plot_bollinger',
|
|
44
|
-
'plot_rsi',
|
|
45
|
-
'plot_moving_averages',
|
|
46
|
-
|
|
47
|
-
# Dashboard
|
|
48
|
-
'launch_dashboard'
|
|
49
|
-
]
|
|
50
|
-
except ImportError as e:
|
|
51
|
-
# Dashboard dependencies (tkinter, tkcalendar) may not be installed
|
|
52
|
-
print(f"Warning: Dashboard not available. Install with: pip install tkcalendar")
|
|
53
|
-
__all__ = [
|
|
54
|
-
# Data fetching
|
|
55
|
-
'fetch_data',
|
|
56
|
-
|
|
57
|
-
# Indicators
|
|
58
|
-
'Rsi',
|
|
59
|
-
'bb_bands',
|
|
60
|
-
'macd',
|
|
61
|
-
'atr',
|
|
62
|
-
'sma',
|
|
63
|
-
'ema',
|
|
64
|
-
'demma',
|
|
65
|
-
'temma',
|
|
66
|
-
'RVWAP',
|
|
67
|
-
|
|
68
|
-
# Visualization
|
|
69
|
-
'plot_candlestick',
|
|
70
|
-
'plot_macd',
|
|
71
|
-
'plot_bollinger',
|
|
72
|
-
'plot_rsi',
|
|
73
|
-
'plot_moving_averages'
|
|
74
|
-
]
|
|
75
|
-
|
|
76
|
-
__version__ = '2.0.0'
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|