Skip to content

Commit

Permalink
bug fixed: price_board function for VCI data source
Browse files Browse the repository at this point in the history
  • Loading branch information
thinh-vu committed Jul 14, 2024
1 parent 2ce81d3 commit b57647b
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 49 deletions.
4 changes: 2 additions & 2 deletions vnstock3/explorer/vci/company.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
return data
20 changes: 10 additions & 10 deletions vnstock3/explorer/vci/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
'1W' : 'ONE_DAY',
'1M' : 'ONE_DAY'
}

_RESAMPLE_MAP = {
'5m' : '5min',
'15m' : '15min',
Expand All @@ -28,7 +28,7 @@
'l': 'low',
'c': 'close',
'v': 'volume',
}
}


# Pandas data type mapping for history price data
Expand All @@ -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'
}
Expand All @@ -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'}
_UNIT_MAP = {'BILLION':'tỷ', 'PERCENT':'%', 'INDEX':'index', 'MILLION':'triệu'}
22 changes: 11 additions & 11 deletions vnstock3/explorer/vci/financial.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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'
]

Expand Down Expand Up @@ -146,15 +146,15 @@ 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ệ']:
raise ValueError("Báo cáo không hợp lệ. Chỉ chấp nhận '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ệ'.")

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
Expand All @@ -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]
Expand All @@ -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
return financial_report
29 changes: 13 additions & 16 deletions vnstock3/explorer/vci/quote.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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.
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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())
Expand Down Expand Up @@ -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.
Expand All @@ -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
Expand All @@ -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:
Expand All @@ -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)

Expand Down Expand Up @@ -264,6 +264,3 @@ def _as_df(self, history_data: Dict, asset_type: str, interval: str, floating: O
df.source = "VCI"

return df



23 changes: 13 additions & 10 deletions vnstock3/explorer/vci/trading.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)

Expand Down

0 comments on commit b57647b

Please sign in to comment.