quant-agent 0.3.0__py3-none-any.whl

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.
@@ -0,0 +1,25 @@
1
+ """
2
+ quant_agent (quant-agent): Vietnamese stock market data, read-only, no account/API key needed.
3
+
4
+ Every function below is exposed at the top level (`quant_agent.stock_historical_data(...)`,
5
+ etc.) via the re-exports below, so submodule names (technical, fundamental, ...) are
6
+ an internal organizing detail, not part of the public API contract.
7
+
8
+ The one exception is the `broker` subpackage (e.g. `DNSEClient`): it performs
9
+ real authenticated trading (login, place_order, cancel_order) against a real
10
+ brokerage account, a fundamentally different risk profile from the rest of
11
+ this read-only library. It's re-exported here too for backward
12
+ compatibility, but see `broker/__init__.py` before using it.
13
+ """
14
+
15
+ __author__ = "quant-agent"
16
+
17
+ from .config import * # date helpers + re-exports the `sources` subpackage
18
+ from .utils import *
19
+ from .fundamental import * # stock listing, company profile, financials
20
+ from .technical import * # historical/intraday OHLC price data
21
+ from .trading import * # price board, price depth, intraday trades
22
+ from .funds import * # mutual fund data
23
+ from .chart import * # candlestick / Bollinger Bands visualization
24
+ from .integration import * # third-party data export (Amibroker)
25
+ from .broker import * # authenticated brokerage trading clients - see broker/__init__.py
@@ -0,0 +1,20 @@
1
+ """
2
+ Authenticated brokerage trading clients - login, place/cancel real orders,
3
+ check balances. A DIFFERENT RISK CATEGORY from every other package in
4
+ quant_agent, which is read-only market data requiring no account or credentials.
5
+ Nothing here is called automatically; each client must be constructed and
6
+ logged in explicitly.
7
+
8
+ To add a new broker: create `broker/<name>.py` with its own client class
9
+ (see `dnse.py` for the shape: `login`, `place_order`, `cancel_order`,
10
+ account/balance getters), then add one line below (both the
11
+ `from .<name> import *` AND its client class name in `__all__` - see
12
+ `sources/__init__.py` for why `__all__` matters here). There's no shared
13
+ base class yet since only one broker is implemented so far - once a second
14
+ one lands, factor out whatever actually turns out to be common (e.g. the
15
+ Bearer-token-per-request pattern) rather than guessing at an interface now.
16
+ """
17
+
18
+ from .dnse import *
19
+
20
+ __all__ = ['DNSEClient']
@@ -0,0 +1,257 @@
1
+ # DNSE Lightspeed API: https://www.dnse.vn
2
+ """
3
+ !!! DIFFERENT RISK CATEGORY FROM THE REST OF THIS LIBRARY !!!
4
+
5
+ Every other module in quant_agent is read-only market data (prices, financials,
6
+ listings). `DNSEClient` below is NOT: it authenticates with real DNSE
7
+ brokerage credentials and can PLACE and CANCEL REAL ORDERS
8
+ (`place_order`, `cancel_order`) against a real trading account with real
9
+ money.
10
+
11
+ Nothing in this file is called automatically by any other part of quant_agent -
12
+ you must construct a `DNSEClient`, call `.login(...)`, and invoke its
13
+ methods explicitly to do anything with it.
14
+ """
15
+
16
+ from ..config import *
17
+
18
+
19
+ class DNSEClient:
20
+ def __init__(self):
21
+ self.token = None
22
+ self.trading_token = None
23
+
24
+ def login(self, user_name, password):
25
+ """
26
+ Authenticate the user and obtain a JWT token for further API requests.
27
+
28
+ Args:
29
+ user_name (str): DNSE username. Can be 064CXXXXX, your email, or your phone number.
30
+ password (str): Your DNSE password.
31
+
32
+ Returns:
33
+ str: JWT token if authentication is successful, None otherwise.
34
+ """
35
+ url = "https://services.entrade.com.vn/dnse-user-service/api/auth"
36
+ payload = json.dumps({'username': user_name, 'password': password})
37
+ headers = {'Content-Type': 'application/json'}
38
+ data = fetch_json('POST', url, headers=headers, data=payload, source='DNSE')
39
+ if data is None:
40
+ print('Login failed')
41
+ return None
42
+ print('Login successfully')
43
+ self.token = data['token']
44
+ return self.token
45
+
46
+ def account(self):
47
+ """
48
+ Get the full user profile from DNSE.
49
+
50
+ Returns:
51
+ DataFrame: A DataFrame containing the user profile if successful, None otherwise.
52
+ """
53
+ url = "https://services.entrade.com.vn/dnse-user-service/api/me"
54
+ headers = {'Content-Type': 'text/plain', 'Authorization': f'Bearer {self.token}'}
55
+ data = fetch_json('GET', url, headers=headers, data="N/A", source='DNSE')
56
+ if data is None:
57
+ print('Get profile failed')
58
+ return None
59
+ print('Get profile successfully')
60
+ return pd.DataFrame(data, index=[0])
61
+
62
+ def sub_accounts(self):
63
+ """
64
+ Get sub-accounts information.
65
+
66
+ Returns:
67
+ DataFrame: A DataFrame containing the sub-accounts information if successful, None otherwise.
68
+ """
69
+ url = "https://services.entrade.com.vn/dnse-order-service/accounts"
70
+ headers = {"Authorization": f"Bearer {self.token}"}
71
+ data = fetch_json('GET', url, headers=headers, source='DNSE')
72
+ return json_normalize(data['accounts']) if data is not None else None
73
+
74
+ def account_balance (self, sub_account):
75
+ """
76
+ Get account balance for a specific sub-account.
77
+
78
+ Args:
79
+ sub_account (str): DNSE sub account number (mã tiểu khoản).
80
+ """
81
+ url = f"https://services.entrade.com.vn/dnse-order-service/account-balances/{sub_account}"
82
+ headers = {"Authorization": f"Bearer {self.token}"}
83
+ data = fetch_json('GET', url, headers=headers, source='DNSE')
84
+ return json_normalize(data) if data is not None else None
85
+
86
+ def email_otp(self):
87
+ """
88
+ Trigger an email OTP request to DNSE. The OTP will be sent to the email address associated with the DNSE account.
89
+
90
+ Returns:
91
+ None.
92
+ """
93
+ url = "https://services.entrade.com.vn/dnse-auth-service/api/email-otp"
94
+ headers = {"Authorization": f"Bearer {self.token}"}
95
+ response = fetch('GET', url, headers=headers, source='DNSE')
96
+ if response is not None:
97
+ print("OTP sent to email")
98
+
99
+ def get_trading_token(self, otp, smart_otp=True):
100
+ """
101
+ Authenticate using OTP and get the trading token.
102
+
103
+ Args:
104
+ otp (str): OTP code for authentication. Input as a string.
105
+
106
+ Returns:
107
+ str: Trading token if authentication is successful, None otherwise.
108
+ """
109
+ if smart_otp:
110
+ url = "https://services.entrade.com.vn/dnse-order-service/trading-token"
111
+ headers = {"Authorization": f"Bearer {self.token}", "smart-otp": otp}
112
+ else:
113
+ url = "https://services.entrade.com.vn/dnse-auth-service/api/email-otp"
114
+ headers = {"Authorization": f"Bearer {self.token}", "otp": otp}
115
+ data = fetch_json('POST', url, headers=headers, source='DNSE')
116
+ if data is None:
117
+ print("Error authenticating")
118
+ return None
119
+ self.trading_token = data.get("tradingToken")
120
+ print("Authenticated! Trading token returned.")
121
+ return self.trading_token
122
+
123
+ def loan_packages(self, sub_account, asset_type='stock'):
124
+ """
125
+ Get the list of loan packages for a specific sub account.
126
+
127
+ Args:
128
+ sub_account (str): DNSE sub account number (mã tiểu khoản).
129
+ asset_type (str): Asset type, either 'stock' or 'derivative'.
130
+
131
+ Returns:
132
+ DataFrame: A DataFrame containing the list of loan packages if successful, None otherwise.
133
+ """
134
+ suffix = 'loan-packages' if asset_type == 'stock' else 'derivative-loan-packages'
135
+ url = f"https://services.entrade.com.vn/dnse-order-service/accounts/{sub_account}/{suffix}"
136
+ headers = {"Authorization": f"Bearer {self.token}"}
137
+ data = fetch_json('GET', url, headers=headers, source='DNSE')
138
+ return json_normalize(data['loanPackages']) if data is not None else None
139
+
140
+ def trade_capacities(self, symbol, price, sub_account, asset_type='stock', loan_package_id=None):
141
+ """
142
+ Get the list of trade capacities (sức mua/bán) for a specific sub account.
143
+
144
+ Args:
145
+ symbol (str): Symbol of the asset.
146
+ price (float): Price of the asset, unit is VND.
147
+ sub_account (str): DNSE sub account number (mã tiểu khoản).
148
+ asset_type (str): Asset type, either 'stock' or 'derivative'.
149
+ loan_package_id (int): Loan package ID (if applicable).
150
+ Returns:
151
+ DataFrame: A DataFrame containing the list of trade capacities if successful, None otherwise.
152
+ """
153
+ if asset_type == 'stock':
154
+ url = f'https://services.entrade.com.vn/dnse-order-service/accounts/{sub_account}/ppse'
155
+ query_params = {"symbol": symbol, "price": price, "loanPackageId": loan_package_id}
156
+ query_params = {k: v for k, v in query_params.items() if v is not None}
157
+ if query_params:
158
+ url += "?" + "&".join(f"{key}={value}" for key, value in query_params.items())
159
+ else:
160
+ url = f'https://services.entrade.com.vn/dnse-order-service/accounts/{sub_account}/derivative-ppse?symbol={symbol}&price={price}&loanPackageId={loan_package_id}'
161
+ headers = {"Authorization": f"Bearer {self.token}"}
162
+ data = fetch_json('GET', url, headers=headers, source='DNSE')
163
+ return json_normalize(data) if data is not None else None
164
+
165
+ def place_order(self, sub_account, symbol, side, quantity, price, order_type, loan_package_id, asset_type='stock'):
166
+ """
167
+ Place an order for stocks or derivatives. THIS SENDS A REAL ORDER TO A REAL
168
+ BROKERAGE ACCOUNT - it is not a simulation.
169
+
170
+ Args:
171
+ sub_account (str): Sub account number.
172
+ symbol (str): Symbol of the asset.
173
+ side (str): 'buy' or 'sell'.
174
+ quantity (int): Order quantity.
175
+ price (float): Order price.
176
+ loan_package_id (int): Loan package ID (if applicable).
177
+ asset_type (str): Asset type, either 'stock' or 'derivative'.
178
+
179
+ Returns:
180
+ DataFrame: A DataFrame containing the order information if successful, None otherwise.
181
+ """
182
+ side_code = 'NB' if side == 'buy' else 'NS'
183
+ url = ("https://services.entrade.com.vn/dnse-order-service/v2/orders" if asset_type == 'stock'
184
+ else "https://services.entrade.com.vn/dnse-order-service/derivative/orders")
185
+ headers = {"Authorization": f"Bearer {self.token}", "Trading-Token": self.trading_token}
186
+ payload = {
187
+ "accountNo": sub_account, "symbol": symbol, "side": side_code, "quantity": quantity,
188
+ "price": price, 'orderType': order_type, "loanPackageId": loan_package_id,
189
+ }
190
+ data = fetch_json('POST', url, headers=headers, json=payload, source='DNSE')
191
+ return json_normalize(data) if data is not None else None
192
+
193
+ def order_list(self, sub_account, asset_type='stock'):
194
+ """
195
+ Get the list of orders for a specific account.
196
+
197
+ Args:
198
+ sub_account (str): DNSE sub account number (mã tiểu khoản).
199
+ asset_type (str): Asset type, either 'stock' or 'derivative'.
200
+
201
+ Returns:
202
+ DataFrame: A DataFrame containing the list of orders if successful, None otherwise.
203
+ """
204
+ suffix = 'v2/orders' if asset_type == 'stock' else 'derivative/orders'
205
+ url = f"https://services.entrade.com.vn/dnse-order-service/{suffix}?accountNo={sub_account}"
206
+ headers = {"Authorization": f"Bearer {self.token}"}
207
+ data = fetch_json('GET', url, headers=headers, source='DNSE')
208
+ if data is None:
209
+ return None
210
+ print('Order list retrieved')
211
+ return json_normalize(data['orders'])
212
+
213
+ def order_detail(self, order_id, sub_account, asset_type='stock'):
214
+ """
215
+ Get the details of a specific order for a specific sub account.
216
+ Args:
217
+ order_id (str): Order ID.
218
+ sub_account (str): DNSE sub account number (mã tiểu khoản).
219
+ asset_type (str): Asset type, either 'stock' or 'derivative'.
220
+ """
221
+ suffix = 'v2/orders' if asset_type == 'stock' else 'derivative/orders'
222
+ url = f"https://services.entrade.com.vn/dnse-order-service/{suffix}/{order_id}?accountNo={sub_account}"
223
+ headers = {"Authorization": f"Bearer {self.token}"}
224
+ data = fetch_json('GET', url, headers=headers, source='DNSE')
225
+ return json_normalize(data) if data is not None else None
226
+
227
+ def cancel_order (self, order_id, sub_account, asset_type='stock'):
228
+ """
229
+ Cancel an order. THIS AFFECTS A REAL ORDER ON A REAL BROKERAGE ACCOUNT.
230
+ Args:
231
+ order_id (str): Order ID.
232
+ sub_account (str): DNSE sub account number (mã tiểu khoản).
233
+ asset_type (str): Asset type, either 'stock' or 'derivative'.
234
+ """
235
+ suffix = 'v2/orders' if asset_type == 'stock' else 'derivative/orders'
236
+ url = f"https://services.entrade.com.vn/dnse-order-service/{suffix}/{order_id}?accountNo={sub_account}"
237
+ headers = {"Authorization": f"Bearer {self.token}"}
238
+ response = fetch('DELETE', url, headers=headers, source='DNSE')
239
+ if response is None:
240
+ return None
241
+ print("Order cancelled")
242
+ return json_normalize(response.json())
243
+
244
+ def deals_list (self, sub_account, asset_type='stock'):
245
+ """
246
+ Get the list of deals for a specific sub account.
247
+ Args:
248
+ sub_account (str): DNSE sub account number (mã tiểu khoản).
249
+ """
250
+ url = (f'https://services.entrade.com.vn/dnse-deal-service/deals?accountNo={sub_account}' if asset_type == 'stock'
251
+ else f'https://services.entrade.com.vn/dnse-derivative-core/deals?accountNo={sub_account}')
252
+ headers = {"Authorization": f"Bearer {self.token}"}
253
+ data = fetch_json('GET', url, headers=headers, source='DNSE')
254
+ if data is None:
255
+ return None
256
+ print('Deals list retrieved')
257
+ return json_normalize(data['data'])
quant_agent/chart.py ADDED
@@ -0,0 +1,235 @@
1
+ """
2
+ Plotly visualizations built on top of the OHLC DataFrame shape returned by
3
+ `technical.stock_historical_data()` (columns: time, open, high, low, close,
4
+ volume, ticker). Pure computation/plotting - no network calls, so nothing
5
+ here is affected by any data source's availability.
6
+ """
7
+
8
+ from .config import *
9
+ from .technical import *
10
+
11
+ import plotly.graph_objs as go
12
+
13
+
14
+ # CANDLESTICK CHART
15
+ def candlestick_chart(df, title='Candlestick Chart with MA and Volume', x_label='Date', y_label='Price', ma_periods=None, show_volume=True, figure_size=(15, 8), reference_period=None, colors=('#00F4B0', '#FF3747'), reference_colors=('blue', 'black')):
16
+ """
17
+ Generate a candlestick chart with optional Moving Averages (MA) lines, volume data, and reference lines.
18
+
19
+ Parameters:
20
+ - df: DataFrame with candlestick data ('time', 'open', 'high', 'low', 'close', 'volume', 'ticker').
21
+ - title: Title of the chart.
22
+ - x_label: Label for the x-axis.
23
+ - y_label: Label for the y-axis.
24
+ - ma_periods: List of MA periods to calculate and plot (e.g., [10, 50, 200]).
25
+ - show_volume: Boolean to indicate whether to display volume data.
26
+ - figure_size: Tuple specifying the figure size (width, height).
27
+ - reference_period: Number of days to consider for reference lines (e.g., 90).
28
+ - colors: Tuple of color codes for up and down candles (e.g., ('#00F4B0', '#FF3747')).
29
+ - reference_colors: Tuple of color codes for reference lines (e.g., ('black', 'blue')).
30
+
31
+ Returns:
32
+ - Plotly figure object.
33
+ """
34
+ # Create the base candlestick chart
35
+ candlestick_trace = go.Candlestick(
36
+ x=df['time'],
37
+ open=df['open'],
38
+ high=df['high'],
39
+ low=df['low'],
40
+ close=df['close'],
41
+ name='Candlestick',
42
+ )
43
+
44
+ # Create a figure
45
+ fig = go.Figure(data=[candlestick_trace])
46
+
47
+ # Add volume data if specified
48
+ if show_volume:
49
+ volume_trace = go.Bar(
50
+ x=df['time'],
51
+ y=df['volume'],
52
+ name='Volume',
53
+ yaxis='y2', # Use the secondary y-axis for volume
54
+ marker=dict(color=[colors[0] if close >= open else colors[1] for close, open in zip(df['close'], df['open'])]), # Match volume color to candle color
55
+ )
56
+
57
+ fig.add_trace(volume_trace)
58
+
59
+ # Add Moving Averages (MA) lines if specified
60
+ if ma_periods:
61
+ for period in ma_periods:
62
+ ma_name = f'{period}-day MA'
63
+ df[ma_name] = df['close'].rolling(period).mean()
64
+
65
+ ma_trace = go.Scatter(
66
+ x=df['time'],
67
+ y=df[ma_name],
68
+ mode='lines',
69
+ name=ma_name,
70
+ )
71
+
72
+ fig.add_trace(ma_trace)
73
+
74
+ # Add straight reference lines for the highest high and lowest low
75
+ if reference_period:
76
+ df['lowest_low'] = df['low'].rolling(reference_period).min()
77
+ df['highest_high'] = df['high'].rolling(reference_period).max()
78
+
79
+ lowest_low_trace = go.Scatter(
80
+ x=df['time'],
81
+ y=[df['lowest_low'].iloc[-1]] * len(df), # Create a straight line for lowest low
82
+ mode='lines',
83
+ name=f'Lowest Low ({reference_period} days)',
84
+ line=dict(color=reference_colors[0], dash='dot'),
85
+ )
86
+
87
+ highest_high_trace = go.Scatter(
88
+ x=df['time'],
89
+ y=[df['highest_high'].iloc[-1]] * len(df), # Create a straight line for highest high
90
+ mode='lines',
91
+ name=f'Highest High ({reference_period} days)',
92
+ line=dict(color=reference_colors[1], dash='dot'),
93
+ )
94
+
95
+ fig.add_trace(lowest_low_trace)
96
+ fig.add_trace(highest_high_trace)
97
+
98
+ # Customize the chart appearance
99
+ fig.update_layout(
100
+ title=title,
101
+ xaxis_title=x_label,
102
+ yaxis_title=y_label,
103
+ xaxis_rangeslider_visible=True,
104
+ yaxis2=dict(
105
+ title='Volume',
106
+ overlaying='y',
107
+ side='right',
108
+ ),
109
+ width=figure_size[0] * 100, # Convert short form to a larger size for better readability
110
+ height=figure_size[1] * 100,
111
+ margin=dict(l=50, r=50, t=70, b=50), # Adjust margins for space between title and legend
112
+ )
113
+
114
+ return fig
115
+
116
+ # BOLLINGER BANDS
117
+
118
+ def bollinger_bands(df, window=20, num_std_dev=2):
119
+ """
120
+ Calculate Bollinger Bands for a DataFrame.
121
+
122
+ Parameters:
123
+ - df: DataFrame with OHLC data ('time', 'open', 'high', 'low', 'close', 'volume', 'ticker').
124
+ - window: The rolling window size for calculating the moving average and standard deviation.
125
+ - num_std_dev: The number of standard deviations to use for the Bollinger Bands.
126
+
127
+ Returns:
128
+ - DataFrame with Bollinger Bands ('time', 'upper_band', 'middle_band', 'lower_band').
129
+ """
130
+ df['middle_band'] = df['close'].rolling(window=window).mean()
131
+ df['rolling_std'] = df['close'].rolling(window=window).std()
132
+ df['upper_band'] = df['middle_band'] + (num_std_dev * df['rolling_std'])
133
+ df['lower_band'] = df['middle_band'] - (num_std_dev * df['rolling_std'])
134
+ df.drop(columns=['rolling_std'], inplace=True)
135
+ return df
136
+
137
+ def bollinger_bands_chart(df, use_candlestick=True, show_volume=True, fig_size=(15, 8), chart_title='Bollinger Bands Chart', xaxis_title='Date', yaxis_title='Price', bollinger_band_colors=('gray', 'orange', 'gray'), volume_colors=('#00F4B0', '#FF3747')):
138
+ """
139
+ Visualize a candlestick chart or close price chart with Bollinger Bands and volume using Plotly.
140
+
141
+ Parameters:
142
+ - df: DataFrame with Bollinger Bands data ('time', 'open', 'high', 'low', 'close', 'volume', 'ticker', 'upper_band', 'middle_band', 'lower_band').
143
+ - use_candlestick: Boolean to indicate whether to use candlestick chart (default) or close price chart.
144
+ - show_volume: Boolean to indicate whether to display volume data on the main chart.
145
+ - fig_size: Tuple specifying the figure size in short form, e.g., (15, 8) equals to (1500, 800) in actual.
146
+ - chart_title: Title for the chart.
147
+ - xaxis_title: Title for the x-axis.
148
+ - yaxis_title: Title for the y-axis.
149
+ - bollinger_band_colors: Tuple of color codes for the Bollinger Bands (upper, middle, lower).
150
+ - volume_colors: Tuple of color codes for volume bars on up and down days.
151
+
152
+ Returns:
153
+ - Plotly figure object.
154
+ """
155
+ fig = go.Figure()
156
+
157
+ if use_candlestick:
158
+ # Create the candlestick chart
159
+ candlestick_trace = go.Candlestick(
160
+ x=df['time'],
161
+ open=df['open'],
162
+ high=df['high'],
163
+ low=df['low'],
164
+ close=df['close'],
165
+ name='Candlestick',
166
+ )
167
+
168
+ fig.add_trace(candlestick_trace)
169
+ else:
170
+ # Create a chart using close prices
171
+ close_price_trace = go.Scatter(
172
+ x=df['time'],
173
+ y=df['close'],
174
+ mode='lines',
175
+ name='Close Price',
176
+ )
177
+
178
+ fig.add_trace(close_price_trace)
179
+
180
+ # Create the Bollinger Bands traces
181
+ upper_band_trace = go.Scatter(
182
+ x=df['time'],
183
+ y=df['upper_band'],
184
+ mode='lines',
185
+ line=dict(color=bollinger_band_colors[0]),
186
+ name='Upper Bollinger Band',
187
+ )
188
+
189
+ middle_band_trace = go.Scatter(
190
+ x=df['time'],
191
+ y=df['middle_band'],
192
+ mode='lines',
193
+ line=dict(color=bollinger_band_colors[1]),
194
+ name='Middle Bollinger Band',
195
+ )
196
+
197
+ lower_band_trace = go.Scatter(
198
+ x=df['time'],
199
+ y=df['lower_band'],
200
+ mode='lines',
201
+ line=dict(color=bollinger_band_colors[2]),
202
+ name='Lower Bollinger Band',
203
+ )
204
+
205
+ fig.add_trace(upper_band_trace)
206
+ fig.add_trace(middle_band_trace)
207
+ fig.add_trace(lower_band_trace)
208
+
209
+ if show_volume:
210
+ # Create the volume bars with different colors for up and down days
211
+ volume_color = [volume_colors[0] if close >= open else volume_colors[1] for close, open in zip(df['close'], df['open'])]
212
+
213
+ volume_trace = go.Bar(
214
+ x=df['time'],
215
+ y=df['volume'],
216
+ name='Volume',
217
+ marker=dict(color=volume_color),
218
+ yaxis='y2',
219
+ )
220
+
221
+ fig.add_trace(volume_trace)
222
+
223
+ # Customize the chart appearance
224
+ fig.update_layout(
225
+ title=chart_title,
226
+ xaxis_title=xaxis_title,
227
+ yaxis_title=yaxis_title,
228
+ xaxis_rangeslider_visible=True,
229
+ # legend=dict(orientation="h", y=1.05),
230
+ yaxis2=dict(title='Volume', overlaying='y', side='right'),
231
+ width=fig_size[0] * 100, # Convert short form width to full width
232
+ height=fig_size[1] * 100, # Convert short form height to full height
233
+ )
234
+
235
+ return fig
quant_agent/config.py ADDED
@@ -0,0 +1,60 @@
1
+
2
+ """
3
+ Date/time helpers used across quant_agent as default values for `start_date`/
4
+ `end_date` parameters, plus a re-export hub: every other module in this
5
+ package does `from .config import *` and expects headers (ssi_headers,
6
+ vci_headers, ...) and HTTP helpers (fetch_json, ...) to be
7
+ available from here. Those actually live in the `sources` subpackage (one
8
+ file per data source, so adding a new source never means editing a shared
9
+ file) - re-exporting them here keeps every module's existing import line
10
+ working unchanged.
11
+ """
12
+
13
+ import json
14
+ import time
15
+ from datetime import datetime, timedelta
16
+
17
+ import pandas as pd
18
+ import requests
19
+ from pandas import json_normalize
20
+
21
+ # Re-exported for backward compatibility - see the `sources` subpackage.
22
+ from .sources import *
23
+
24
+
25
+ def api_request(url, headers=ssi_headers):
26
+ r = requests.get(url, headers).json()
27
+ return r
28
+
29
+
30
+ # TRADING INTELLIGENT
31
+ today_val = datetime.now()
32
+
33
+ def today():
34
+ return today_val.strftime('%Y-%m-%d')
35
+
36
+ def last_xd (day_num): # return the date of last x days
37
+ """
38
+ This function returns the date that X days ago from today in the format of YYYY-MM-DD.
39
+ Args:
40
+ day_num (:obj:`int`, required): numer of days.
41
+ Returns:
42
+ :obj:`str`:
43
+ 2022-02-22
44
+ Raises:
45
+ ValueError: raised whenever any of the introduced arguments is not valid.
46
+ """
47
+ return (today_val - timedelta(day_num)).strftime('%Y-%m-%d')
48
+
49
+ def start_xm (period): # return the start date of x months
50
+ """
51
+ This function returns the start date of X months ago from today in the format of YYYY-MM-DD.
52
+ Args:
53
+ period (:obj:`int`, required): numer of months (period).
54
+ Returns:
55
+ :obj:`str`:
56
+ 2022-01-01
57
+ Raises:
58
+ ValueError: raised whenever any of the introduced arguments is not valid.
59
+ """
60
+ return pd.date_range(end=today(), periods=period+1, freq='MS')[0].strftime('%Y-%m-%d')
@@ -0,0 +1 @@
1
+ # To be updated soon