Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add dateutil in requirements and bugfix in testfile and Corporate Actions. #54

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
Open
3 changes: 2 additions & 1 deletion __init__.py → nsetools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,6 @@
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"""
__VERSION__='1.0.11'
__VERSION__ = '1.0.11'

from .nse import Nse
File renamed without changes.
34 changes: 19 additions & 15 deletions datemgr.py → nsetools/datemgr.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,33 +7,34 @@

def get_nearest_business_day(d):
""" takes datetime object"""
if d.isoweekday() is 7 or d.isoweekday() is 6:
if d.isoweekday() == 7 or d.isoweekday() == 6:
d = d - relativedelta(days=1)
return get_nearest_business_day(d)

# republic day
elif d.month is 1 and d.day is 26:
elif d.month == 1 and d.day == 26:
d = d - relativedelta(days=1)
return get_nearest_business_day(d)
# labour day
elif d.month is 5 and d.day is 1:
elif d.month == 5 and d.day == 1:
d = d - relativedelta(days=1)
return get_nearest_business_day(d)
# independece day
elif d.month is 8 and d.day is 15:
elif d.month == 8 and d.day == 15:
d = d - relativedelta(days=1)
return get_nearest_business_day(d)
# Gandhi Jayanti
elif d.month is 10 and d.day is 2:
elif d.month == 10 and d.day == 2:
d = d - relativedelta(days=1)
return get_nearest_business_day(d)
# chirstmas
elif d.month is 12 and d.day is 25:
elif d.month == 12 and d.day == 25:
d = d - relativedelta(days=1)
return get_nearest_business_day(d)
else:
return d


def is_known_holiday(d):
"""accepts datetime/date object and returns boolean"""
if type(d) == dt.datetime:
Expand All @@ -42,35 +43,36 @@ def is_known_holiday(d):
raise DateFormatError("only date objects or datetime objects")
else:
# fine do nothing
pass
pass

# declare the list of holidays here.
# declare the list of holidays here.
# republic day.
if d.month is 1 and d.day is 26:
if d.month == 1 and d.day == 26:
return True
# labour day
elif d.month is 5 and d.day is 1:
elif d.month == 5 and d.day == 1:
d = d - relativedelta(days=1)
return get_nearest_business_day(d)
# independence day
elif d.month is 8 and d.day is 15:
elif d.month == 8 and d.day == 15:
return True
# gandhi jayanti
elif d.month is 10 and d.day is 2:
elif d.month == 10 and d.day == 2:
return True
# christmas
elif d.month is 12 and d.day is 25:
elif d.month == 12 and d.day == 25:
return True
else:
return False


def mkdate(d):
"""tries its best to return a valid date. it can accept pharse like today,
yesterday, day before yesterday etc.
"""
# check if the it is a string
return_date = ""
if type(d) is str:
if type(d) == str:
if d == "today":
return_date = dt.date.today()
elif d == "yesterday":
Expand All @@ -88,11 +90,13 @@ def mkdate(d):
# check if future date.
return return_date


def usable_date(d):
"""accepts fuzzy format and returns most sensible date"""
return get_nearest_business_day(mkdate(d))

def get_date_range(frm, to, skip_dates=[]):

def get_date_range(frm, to):
"""accepts fuzzy format date and returns business adjusted date ranges"""
# for x in rrule.rrule(rrule.DAILY, dtstart=s, until=dt.datetime.now(), byweekday=[0, 1, 2, 3, 4]): print(x)
frm = usable_date(frm)
Expand Down
20 changes: 11 additions & 9 deletions downloader.py → nsetools/downloader.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,14 @@
import zipfile
import datetime as dt
from urllib.request import Request
from nsetools.datemgr import mkdate, usable_date, get_date_range
from nsetools.datemgr import mkdate, get_date_range
from nsetools import Nse
from abc import ABCMeta, abstractmethod


class BaseBhavcopyDownloader(metaclass=ABCMeta):
"""Base class for all types of bhavcopy downloader"""

def __init__(self, from_date, to_date=dt.datetime.now().date(), skip_dates=[]):
"""accepts date in fuzzy format"""
self.bhavcopy_base_url = "https://www.nseindia.com/content/historical/EQUITIES/%s/%s/cm%s%s%sbhav.csv.zip"
Expand All @@ -45,7 +47,7 @@ def __init__(self, from_date, to_date=dt.datetime.now().date(), skip_dates=[]):

def generate_dates(self):
return get_date_range(self.from_date, self.to_date, skip_dates=self.skip_dates)

def get_bhavcopy_url(self, d):
"""accept date and return bhavcopy url"""
day_of_month = d.strftime("%d")
Expand Down Expand Up @@ -78,11 +80,11 @@ def download_one(self, d):

@abstractmethod
def download(self):
pass
pass

@abstractmethod
def update(self):
pass
pass


class BhavcopyFileSystemDownloader(BaseBhavcopyDownloader):
Expand All @@ -99,18 +101,18 @@ def download(self):
try:
content = self.download_one(date)
except Exception as err:
print("unable to download for the date: %s" % date.strftime("%Y-%m-%d"))
print("unable to download for the date: %s" % date.strftime("%Y-%m-%d"))
else:
fh = open(self.directory + "/" + date.strftime("%Y-%m-%d") + ".csv", "w")
fh.write(content)
fh.close()

def update(self):
pass
pass


if __name__ == '__main__':
b = BhavcopyFileSystemDownloader(directory="/tmp/bhavcopy", from_date="01-01-2018")
b.download()

# https://stackoverflow.com/questions/49183801/ssl-certificate-verify-failed-with-urllib
# https://stackoverflow.com/questions/49183801/ssl-certificate-verify-failed-with-urllib
3 changes: 2 additions & 1 deletion errors.py → nsetools/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ class BhavcopyNotAvailableError(Exception):
when the market was close"""
pass


class DateFormatError(Exception):
"""in case the date format is errorneous"""
pass
pass
69 changes: 61 additions & 8 deletions nse.py → nsetools/nse.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,14 @@

"""
import six
import ast
import re
import json
import zipfile
import io
from dateutil import parser
from nsetools.bases import AbstractBaseExchange
from nsetools.utils import byte_adaptor
from nsetools.utils import js_adaptor
from nsetools.datemgr import mkdate

# import paths differ in python 2 and python 3
if six.PY2:
from urllib2 import build_opener, HTTPCookieProcessor, Request
Expand All @@ -42,8 +41,6 @@
from urllib.parse import urlencode
from http.cookiejar import CookieJar

from nsetools.utils import byte_adaptor, js_adaptor
from nsetools.datemgr import mkdate

class Nse(AbstractBaseExchange):
"""
Expand Down Expand Up @@ -76,6 +73,10 @@ def __init__(self):
self.preopen_niftybank_url =\
"https://www1.nseindia.com/live_market/dynaContent/live_analysis/pre_open/niftybank.json"
self.fno_lot_size_url = "https://www1.nseindia.com/content/fo/fo_mktlots.csv"
self.bhavcopyPR_base_url = "https://www1.nseindia.com/archives/equities/bhavcopy/pr/PR%s%s%s.zip"
self.corp_act_base_filename = "Bc%s%s%s.csv"
self.daily_volatility_files = "https://www1.nseindia.com/archives/nsccl/volt/FOVOLT_%s%s%s.csv"
self.daily_volatility_filename = "FOVOLT_%s%s%s.csv"

def get_fno_lot_sizes(self, cached=True, as_json=False):
"""
Expand Down Expand Up @@ -356,8 +357,15 @@ def nse_headers(self):
return {'Accept': '*/*',
'Accept-Language': 'en-US,en;q=0.5',
'Host': 'www1.nseindia.com',
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:28.0) Gecko/20100101 Firefox/28.0',
'X-Requested-With': 'XMLHttpRequest'
'Connection': 'keep-alive',
'Referer': "https://www1.nseindia.com/products/content/equities/equities/archieve_eq.htm",
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:102.0) Gecko/20100101 Firefox/102.0',
'X-Requested-With': 'XMLHttpRequest',
'Sec - Fetch - Dest': 'document',
'Sec - Fetch - Mode': 'navigate',
'Sec - Fetch - Site': 'same - origin',
'Sec - Fetch - User': '?1',
'Sec - GPC': '1'
}

def nse_opener(self):
Expand Down Expand Up @@ -431,6 +439,31 @@ def get_bhavcopy_filename(self, d):
filename = self.bhavcopy_base_filename % (day_of_month, mon, year)
return filename

def get_bhavcopyPR_url(self, d):
"""Take date and return bhavcopyPR zip url. Starts from 4th January 2010."""
d = mkdate(d)
day_of_month = d.strftime("%d")
mon = d.strftime("%m")
year = d.strftime("%y")
url = self.bhavcopyPR_base_url % (day_of_month, mon, year)
return url

def get_corp_act_filename(self, d):
d = mkdate(d)
day_of_month = d.strftime("%d")
mon = d.strftime("%m")
year = d.strftime("%y")
filename = self.corp_act_base_filename % (day_of_month, mon, year)
return filename

def get_daily_volatility_file_url(self, d):
d = mkdate(d)
day_of_month = d.strftime("%d")
mon = d.strftime("%m")
year = d.year
url = self.daily_volatility_files % (day_of_month, mon, year)
return url

def download_bhavcopy(self, d):
"""returns bhavcopy as csv file."""
# ex_url = "https://www.nseindia.com/content/historical/EQUITIES/2011/NOV/cm08NOV2011bhav.csv.zip"
Expand All @@ -444,7 +477,27 @@ def download_bhavcopy(self, d):
result = zf.read(filename)
except KeyError:
result = zf.read(zf.filelist[0].filename)
return zf.read(filename).decode("utf-8")
return result.decode("utf-8")

def download_corp_act(self, d):
"""returns Corporate Actions file as csv file."""
# ex_url = "https://www.nseindia.com/archives/equities/bhavcopy/pr/PR230819.zip"
url = self.get_bhavcopyPR_url(d)
filename = self.get_corp_act_filename(d)
# response = requests.get(url, headers=self.headers)
response = self.opener.open(Request(url, None, self.headers))
zip_file_handle = io.BytesIO(response.read())
zf = zipfile.ZipFile(zip_file_handle)
try:
result = zf.read(filename)
except KeyError:
result = zf.read(zf.filelist[0].filename)
return result.decode('utf-8')

def download_daily_volatility_file(self, d):
url = self.get_daily_volatility_file_url(d)
response = self.opener.open(Request(url, None, self.headers)).read()
return response.decode('utf-8')

def download_index_copy(self, d):
"""returns index copy file"""
Expand Down
Loading