From b57647b2247ba6e7209c5eece6438cb6e3107a61 Mon Sep 17 00:00:00 2001 From: thinh-vu Date: Sun, 14 Jul 2024 13:56:17 +0700 Subject: [PATCH] bug fixed: price_board function for VCI data source --- vnstock3/explorer/vci/company.py | 4 ++-- vnstock3/explorer/vci/const.py | 20 ++++++++++---------- vnstock3/explorer/vci/financial.py | 22 +++++++++++----------- vnstock3/explorer/vci/quote.py | 29 +++++++++++++---------------- vnstock3/explorer/vci/trading.py | 23 +++++++++++++---------- 5 files changed, 49 insertions(+), 49 deletions(-) diff --git a/vnstock3/explorer/vci/company.py b/vnstock3/explorer/vci/company.py index a108e657..75bd3468 100644 --- a/vnstock3/explorer/vci/company.py +++ b/vnstock3/explorer/vci/company.py @@ -2,7 +2,7 @@ import json import requests from typing import Optional -from .const import _BASE_URL, _GRAPHQL_URL, _FINANCIAL_REPORT_PERIOD_MAP, _UNIT_MAPPING +from .const import _BASE_URL, _GRAPHQL_URL, _FINANCIAL_REPORT_PERIOD_MAP, _UNIT_MAP from vnstock3.core.utils.parser import get_asset_type, camel_to_snake from vnstock3.core.utils.logger import get_logger from vnstock3.core.utils.user_agent import get_headers @@ -40,4 +40,4 @@ def _fetch_data(self): if response.status_code != 200: logger.error(f"Request failed with status code {response.status_code}. Details: {response.text}") data = response.json()['data'] - return data \ No newline at end of file + return data diff --git a/vnstock3/explorer/vci/const.py b/vnstock3/explorer/vci/const.py index ccde4268..8cebbf93 100644 --- a/vnstock3/explorer/vci/const.py +++ b/vnstock3/explorer/vci/const.py @@ -12,7 +12,7 @@ '1W' : 'ONE_DAY', '1M' : 'ONE_DAY' } - + _RESAMPLE_MAP = { '5m' : '5min', '15m' : '15min', @@ -28,7 +28,7 @@ 'l': 'low', 'c': 'close', 'v': 'volume', -} +} # Pandas data type mapping for history price data @@ -44,9 +44,9 @@ _GROUP_CODE = ['HOSE', 'VN30', 'VNMidCap', 'VNSmallCap', 'VNAllShare', 'VN100', 'ETF', 'HNX', 'HNX30', 'HNXCon', 'HNXFin', 'HNXLCap', 'HNXMSCap', 'HNXMan', 'UPCOM', 'FU_INDEX', 'FU_BOND', 'BOND', 'CW'] _INTRADAY_MAP = { - 'truncTime':'time', - 'matchPrice':'price', - 'matchVol':'volume', + 'truncTime':'time', + 'matchPrice':'price', + 'matchVol':'volume', 'matchType':'match_type', 'id':'id' } @@ -60,17 +60,17 @@ } _PRICE_DEPTH_MAP = { - 'priceStep':'price', + 'priceStep':'price', 'accumulatedVolume': 'acc_volume', - 'accumulatedBuyVolume' : 'acc_buy_volume', + 'accumulatedBuyVolume' : 'acc_buy_volume', 'accumulatedSellVolume' : 'acc_sell_volume', 'accumulatedUndefinedVolume': 'acc_undefined_volume', } -_FINANCIAL_REPORT_MAP = {'balance_sheet': 'balancesheet', - 'income_statement': 'incomestatement', +_FINANCIAL_REPORT_MAP = {'balance_sheet': 'balancesheet', + 'income_statement': 'incomestatement', 'cash_flow': 'cashflow'} _FINANCIAL_REPORT_PERIOD_MAP = {'year': 'Y', 'quarter': 'Q'} -_UNIT_MAPPING = {'BILLION':'tỷ', 'PERCENT':'%', 'INDEX':'index', 'MILLION':'triệu'} \ No newline at end of file +_UNIT_MAP = {'BILLION':'tỷ', 'PERCENT':'%', 'INDEX':'index', 'MILLION':'triệu'} diff --git a/vnstock3/explorer/vci/financial.py b/vnstock3/explorer/vci/financial.py index abdc9fae..b53bd45b 100644 --- a/vnstock3/explorer/vci/financial.py +++ b/vnstock3/explorer/vci/financial.py @@ -2,7 +2,7 @@ import json import requests from typing import Optional -from .const import _GRAPHQL_URL, _FINANCIAL_REPORT_PERIOD_MAP, _UNIT_MAPPING +from .const import _GRAPHQL_URL, _FINANCIAL_REPORT_PERIOD_MAP, _UNIT_MAP from vnstock3.core.utils.parser import get_asset_type, camel_to_snake from vnstock3.core.utils.logger import get_logger from vnstock3.core.utils.user_agent import get_headers @@ -79,7 +79,7 @@ def _get_report (self, period:Optional[str]=None, lang:Optional[str]='en', show_ target_col_name = 'en_name' # Create a dictionary to map field_name to report type - mapping_df = self._get_ratio_dict(get_all=False) + mapping_df = self._get_ratio_dict(get_all=False) type_mapping = dict(zip(mapping_df[target_col_name], mapping_df['type'])) # add a translation layer mapping for name and en_name @@ -114,8 +114,8 @@ def _get_report (self, period:Optional[str]=None, lang:Optional[str]='en', show_ # Define the primary report types primary_reports = [ - 'Chỉ tiêu cân đối kế toán', - 'Chỉ tiêu lưu chuyển tiền tệ', + 'Chỉ tiêu cân đối kế toán', + 'Chỉ tiêu lưu chuyển tiền tệ', 'Chỉ tiêu kết quả kinh doanh' ] @@ -146,7 +146,7 @@ def multi_level_columns_translate(col, translation_dict): ) return primary_dfs, merged_other_reports\ - + def _process_report (self, report_key:str , period:Optional[str]=None, lang:Optional[str]='en', dropna:Optional[bool]=False, show_log:Optional[bool]=False): # validate report_key should be in 'Chỉ tiêu kết quả kinh doanh', 'Chỉ tiêu cân đối kế toán', 'Chỉ tiêu lưu chuyển tiền tệ' if report_key not in ['Chỉ tiêu kết quả kinh doanh', 'Chỉ tiêu cân đối kế toán', 'Chỉ tiêu lưu chuyển tiền tệ']: @@ -154,7 +154,7 @@ def _process_report (self, report_key:str , period:Optional[str]=None, lang:Opti effective_period = _FINANCIAL_REPORT_PERIOD_MAP.get(period, period) if period else self.period primary_reports = self._get_report(period=effective_period, lang=lang, show_log=show_log)[0] - + balance_sheet_df = primary_reports[report_key] if dropna: # fill NaN values with 0 @@ -168,16 +168,16 @@ def _process_report (self, report_key:str , period:Optional[str]=None, lang:Opti elif lang == 'vi': balance_sheet_df = balance_sheet_df.drop(columns='Kỳ') return balance_sheet_df - + def balance_sheet(self, period:Optional[str]=None, lang:Optional[str]='en', dropna:Optional[bool]=False, show_log:Optional[bool]=False): return self._process_report('Chỉ tiêu cân đối kế toán', period=period, lang=lang, dropna=dropna, show_log=show_log) - + def income_statement(self, period:Optional[str]=None, lang:Optional[str]='en', dropna:Optional[bool]=False, show_log:Optional[bool]=False): return self._process_report('Chỉ tiêu kết quả kinh doanh', period=period, lang=lang, dropna=dropna, show_log=show_log) - + def cash_flow(self, period:Optional[str]=None, lang:Optional[str]='en', dropna:Optional[bool]=False, show_log:Optional[bool]=False): return self._process_report('Chỉ tiêu lưu chuyển tiền tệ', period=period, lang=lang, dropna=dropna, show_log=show_log) - + def ratio(self, period:Optional[str]=None, lang:Optional[str]='en', dropna:Optional[bool]=True, show_log:Optional[bool]=False): effective_period = _FINANCIAL_REPORT_PERIOD_MAP.get(period, period) if period else self.period financial_report = self._get_report(period=effective_period, lang=lang, show_log=show_log)[1] @@ -187,4 +187,4 @@ def ratio(self, period:Optional[str]=None, lang:Optional[str]='en', dropna:Optio # financial_report = financial_report.drop(columns='lengthReport') # elif lang == 'vi': # financial_report = financial_report.drop(columns='Kỳ') - return financial_report \ No newline at end of file + return financial_report diff --git a/vnstock3/explorer/vci/quote.py b/vnstock3/explorer/vci/quote.py index 5421a409..4348588c 100644 --- a/vnstock3/explorer/vci/quote.py +++ b/vnstock3/explorer/vci/quote.py @@ -1,7 +1,7 @@ """History module for VCI.""" # Đồ thị giá, đồ thị dư mua dư bán, đồ thị mức giá vs khối lượng, thống kê hành vi thị tường -from typing import Dict, Optional +from typing import Dict, Optional, Union from datetime import datetime from .const import _BASE_URL, _CHART_URL, _INTERVAL_MAP, _OHLC_MAP, _RESAMPLE_MAP, _OHLC_DTYPE, _INTRADAY_URL, _INTRADAY_MAP, _INTRADAY_DTYPE, _PRICE_DEPTH_MAP from .models import TickerModel @@ -37,16 +37,16 @@ def _input_validation(self, start: str, end: str, interval: str): # if interval is not in the interval_map, raise an error if ticker.interval not in self.interval_map: raise ValueError(f"Giá trị interval không hợp lệ: {ticker.interval}. Vui lòng chọn: 1m, 5m, 15m, 30m, 1H, 1D, 1W, 1M") - + return ticker - def history(self, start: str, end: Optional[str], interval: Optional[str] = "1D", to_df: Optional[bool]=True, show_log: Optional[bool]=False, count_back: Optional[int]=None) -> Dict: + def history(self, start: str, end: Optional[str]=None, interval: Optional[str] = "1D", to_df: Optional[bool]=True, show_log: Optional[bool]=False, count_back: Optional[int]=None) -> Dict: """ Tải lịch sử giá của mã chứng khoán từ nguồn dữ liệu VN Direct. Tham số: - start (bắt buộc): thời gian bắt đầu lấy dữ liệu, có thể là ngày dạng string kiểu "YYYY-MM-DD" hoặc "YYYY-MM-DD HH:MM:SS". - - end (tùy chọn): thời gian kết thúc lấy dữ liệu. Mặc định là None, chương trình tự động lấy thời điểm hiện tại. Có thể nhập ngày dạng string kiểu "YYYY-MM-DD" hoặc "YYYY-MM-DD HH:MM:SS". + - end (tùy chọn): thời gian kết thúc lấy dữ liệu. Mặc định là None, chương trình tự động lấy thời điểm hiện tại. Có thể nhập ngày dạng string kiểu "YYYY-MM-DD" hoặc "YYYY-MM-DD HH:MM:SS". - interval (tùy chọn): Khung thời gian trích xuất dữ liệu giá lịch sử. Giá trị nhận: 1m, 5m, 15m, 30m, 1H, 1D, 1W, 1M. Mặc định là "1D". - to_df (tùy chọn): Chuyển đổi dữ liệu lịch sử trả về dưới dạng DataFrame. Mặc định là True. Đặt là False để trả về dữ liệu dạng JSON. - show_log (tùy chọn): Hiển thị thông tin log giúp debug dễ dàng. Mặc định là False. @@ -67,8 +67,8 @@ def history(self, start: str, end: Optional[str], interval: Optional[str] = "1D" else: end_stamp = int(end_time.timestamp()) - start_stamp = int(start_time.timestamp()) - + start_stamp = int(start_time.timestamp()) + interval = self.interval_map[ticker.interval] # Construct the URL for fetching data @@ -107,7 +107,7 @@ def history(self, start: str, end: Optional[str], interval: Optional[str] = "1D" else: json_data = df.to_json(orient='records') return json_data - + def intraday(self, page_size: Optional[int]=100, last_time: Optional[str]=None, to_df: Optional[bool]=True, show_log: bool=False) -> Dict: """ Truy xuất dữ liệu khớp lệnh của mã chứng khoán bất kỳ từ nguồn dữ liệu VCI @@ -121,7 +121,7 @@ def intraday(self, page_size: Optional[int]=100, last_time: Optional[str]=None, # if self.symbol is not defined, raise ValueError if self.symbol is None: raise ValueError("Vui lòng nhập mã chứng khoán cần truy xuất khi khởi tạo Trading Class.") - + # convert a string to timestamp if last_time is not None: last_time = int(datetime.strptime(last_time, "%Y-%m-%d %H:%M:%S").timestamp()) @@ -171,7 +171,7 @@ def intraday(self, page_size: Optional[int]=100, last_time: Optional[str]=None, else: json_data = df.to_json(orient='records') return json_data - + def price_depth(self, to_df:Optional[bool]=True, show_log:Optional[bool]=False): """ Truy xuất thống kê độ bước giá & khối lượng khớp lệnh của mã chứng khoán bất kỳ từ nguồn dữ liệu VCI. @@ -183,7 +183,7 @@ def price_depth(self, to_df:Optional[bool]=True, show_log:Optional[bool]=False): # if self.symbol is not defined, raise ValueError if self.symbol is None: raise ValueError("Vui lòng nhập mã chứng khoán cần truy xuất khi khởi tạo Trading Class.") - + url = f'{self.base_url}{_INTRADAY_URL}/AccumulatedPriceStepVol/getSymbolData' payload = json.dumps({ "symbol": self.symbol @@ -203,7 +203,7 @@ def price_depth(self, to_df:Optional[bool]=True, show_log:Optional[bool]=False): df = df[_PRICE_DEPTH_MAP.keys()] # rename columns df.rename(columns=_PRICE_DEPTH_MAP, inplace=True) - + df.source = self.data_source if to_df: @@ -229,13 +229,13 @@ def _as_df(self, history_data: Dict, asset_type: str, interval: str, floating: O df = pd.DataFrame(history_data)[columns_of_interest.keys()].rename(columns=_OHLC_MAP) # rearrange columns by open, high, low, close, volume, time df = df[['time', 'open', 'high', 'low', 'close', 'volume']] - + # Ensure 'time' column data are numeric (integers), then convert to datetime df['time'] = pd.to_datetime(df['time'].astype(int), unit='s').dt.tz_localize('UTC') # Localize the original time to UTC # Convert UTC time to Asia/Ho_Chi_Minh timezone, make sure time is correct for minute and hour interval df['time'] = df['time'].dt.tz_convert('Asia/Ho_Chi_Minh') - if asset_type not in ["index", "derivative"]: + if asset_type not in ["index", "derivative"]: # divide open, high, low, close, volume by 1000 df[["open", "high", "low", "close"]] = df[["open", "high", "low", "close"]].div(1000) @@ -264,6 +264,3 @@ def _as_df(self, history_data: Dict, asset_type: str, interval: str, floating: O df.source = "VCI" return df - - - diff --git a/vnstock3/explorer/vci/trading.py b/vnstock3/explorer/vci/trading.py index 487ebbff..bbfbf04d 100644 --- a/vnstock3/explorer/vci/trading.py +++ b/vnstock3/explorer/vci/trading.py @@ -17,7 +17,7 @@ class Trading: """ Truy xuất dữ liệu giao dịch của mã chứng khoán từ nguồn dữ liệu VCI. """ - def __init__(self, symbol:Optional[str], random_agent=False): + def __init__(self, symbol:Optional[str]='VCI', random_agent=False): self.symbol = symbol.upper() self.asset_type = get_asset_type(self.symbol) self.base_url = _BASE_URL @@ -54,15 +54,18 @@ def price_board (self, symbols_list: List[str], to_df:Optional[bool]=True, show_ # Flatten the nested dictionary while preserving the hierarchy in the keys row = flatten_data(item_data) - # Add bid and ask prices and volumes dynamically with hierarchical keys - for i, bid in enumerate(item['bidAsk']['bidPrices'], start=1): - row[f'bidAsk_bid_{i}_price'] = bid['price'] - row[f'bidAsk_bid_{i}_volume'] = bid['volume'] - - for i, ask in enumerate(item['bidAsk']['askPrices'], start=1): - row[f'bidAsk_ask_{i}_price'] = ask['price'] - row[f'bidAsk_ask_{i}_volume'] = ask['volume'] - + try: + # Add bid and ask prices and volumes dynamically with hierarchical keys + for i, bid in enumerate(item['bidAsk']['bidPrices'], start=1): + row[f'bidAsk_bid_{i}_price'] = bid['price'] + row[f'bidAsk_bid_{i}_volume'] = bid['volume'] + + for i, ask in enumerate(item['bidAsk']['askPrices'], start=1): + row[f'bidAsk_ask_{i}_price'] = ask['price'] + row[f'bidAsk_ask_{i}_volume'] = ask['volume'] + except: + pass + # Append the row dictionary to the list rows.append(row)