|
''' |
|
* Project : Screenipy |
|
* Author : Pranjal Joshi |
|
* Created : 28/04/2021 |
|
* Description : Class for managing the user configuration |
|
''' |
|
|
|
import sys |
|
import os |
|
import glob |
|
import re |
|
import configparser |
|
from datetime import date |
|
from classes.ColorText import colorText |
|
|
|
parser = configparser.ConfigParser(strict=False) |
|
|
|
|
|
default_period = '300d' |
|
default_duration = '1d' |
|
|
|
|
|
class tools: |
|
|
|
def __init__(self): |
|
self.consolidationPercentage = 10 |
|
self.volumeRatio = 2 |
|
self.minLTP = 20.0 |
|
self.maxLTP = 50000 |
|
self.period = '300d' |
|
self.duration = '1d' |
|
self.daysToLookback = 30 |
|
self.shuffleEnabled = True |
|
self.cacheEnabled = True |
|
self.stageTwo = False |
|
self.useEMA = False |
|
|
|
def deleteStockData(self,excludeFile=None): |
|
for f in glob.glob('stock_data*.pkl'): |
|
if excludeFile is not None: |
|
if not f.endswith(excludeFile): |
|
os.remove(f) |
|
else: |
|
os.remove(f) |
|
|
|
|
|
|
|
def setConfig(self, parser, default=False, showFileCreatedText=True): |
|
if default: |
|
parser.add_section('config') |
|
parser.set('config', 'period', self.period) |
|
parser.set('config', 'daysToLookback', str(self.daysToLookback)) |
|
parser.set('config', 'duration', self.duration) |
|
parser.set('config', 'minPrice', str(self.minLTP)) |
|
parser.set('config', 'maxPrice', str(self.maxLTP)) |
|
parser.set('config', 'volumeRatio', str(self.volumeRatio)) |
|
parser.set('config', 'consolidationPercentage', |
|
str(self.consolidationPercentage)) |
|
parser.set('config', 'shuffle', 'y') |
|
parser.set('config', 'cacheStockData', 'y') |
|
parser.set('config', 'onlyStageTwoStocks', 'y' if self.stageTwo else 'n') |
|
parser.set('config', 'useEMA', 'y' if self.useEMA else 'n') |
|
try: |
|
fp = open('screenipy.ini', 'w') |
|
parser.write(fp) |
|
fp.close() |
|
if showFileCreatedText: |
|
print(colorText.BOLD + colorText.GREEN + |
|
'[+] Default configuration generated as user configuration is not found!' + colorText.END) |
|
print(colorText.BOLD + colorText.GREEN + |
|
'[+] Use Option > 5 to edit in future.' + colorText.END) |
|
print(colorText.BOLD + colorText.GREEN + |
|
'[+] Close and Restart the program now.' + colorText.END) |
|
input('') |
|
sys.exit(0) |
|
except IOError: |
|
print(colorText.BOLD + colorText.FAIL + |
|
'[+] Failed to save user config. Exiting..' + colorText.END) |
|
input('') |
|
sys.exit(1) |
|
else: |
|
parser = configparser.ConfigParser(strict=False) |
|
parser.add_section('config') |
|
print('') |
|
print(colorText.BOLD + colorText.GREEN + |
|
'[+] Screeni-py User Configuration:' + colorText.END) |
|
self.period = input( |
|
'[+] Enter number of days for which stock data to be downloaded (Days)(Optimal = 365): ') |
|
self.daysToLookback = input( |
|
'[+] Number of recent days (TimeFrame) to screen for Breakout/Consolidation (Days)(Optimal = 20): ') |
|
self.duration = input( |
|
'[+] Enter Duration of each candle (Days)(Optimal = 1): ') |
|
self.minLTP = input( |
|
'[+] Minimum Price of Stock to Buy (in RS)(Optimal = 20): ') |
|
self.maxLTP = input( |
|
'[+] Maximum Price of Stock to Buy (in RS)(Optimal = 50000): ') |
|
self.volumeRatio = input( |
|
'[+] How many times the volume should be more than average for the breakout? (Number)(Optimal = 2.5): ') |
|
self.consolidationPercentage = input( |
|
'[+] How many % the price should be in range to consider it as consolidation? (Number)(Optimal = 10): ') |
|
self.shuffle = str(input( |
|
'[+] Shuffle stocks rather than screening alphabetically? (Y/N): ')).lower() |
|
self.cacheStockData = str(input( |
|
'[+] Enable High-Performance and Data-Saver mode? (This uses little bit more CPU but performs High Performance Screening) (Y/N): ')).lower() |
|
self.stageTwoPrompt = str(input( |
|
'[+] Screen only for Stage-2 stocks?\n(What are the stages? => https://www.investopedia.com/articles/trading/08/stock-cycle-trend-price.asp)\n(Y/N): ')).lower() |
|
self.useEmaPrompt = str(input( |
|
'[+] Use EMA instead of SMA? (EMA is good for Short-term & SMA for Mid/Long-term trades)[Y/N]: ')).lower() |
|
parser.set('config', 'period', self.period + "d") |
|
parser.set('config', 'daysToLookback', self.daysToLookback) |
|
parser.set('config', 'duration', self.duration + "d") |
|
parser.set('config', 'minPrice', self.minLTP) |
|
parser.set('config', 'maxPrice', self.maxLTP) |
|
parser.set('config', 'volumeRatio', self.volumeRatio) |
|
parser.set('config', 'consolidationPercentage', |
|
self.consolidationPercentage) |
|
parser.set('config', 'shuffle', self.shuffle) |
|
parser.set('config', 'cacheStockData', self.cacheStockData) |
|
parser.set('config', 'onlyStageTwoStocks', self.stageTwoPrompt) |
|
parser.set('config', 'useEMA', self.useEmaPrompt) |
|
|
|
|
|
self.deleteStockData() |
|
print(colorText.BOLD + colorText.FAIL + "[+] Cached Stock Data Deleted." + colorText.END) |
|
|
|
try: |
|
fp = open('screenipy.ini', 'w') |
|
parser.write(fp) |
|
fp.close() |
|
print(colorText.BOLD + colorText.GREEN + |
|
'[+] User configuration saved.' + colorText.END) |
|
print(colorText.BOLD + colorText.GREEN + |
|
'[+] Restart the Program to start Screening...' + colorText.END) |
|
input('') |
|
sys.exit(0) |
|
except IOError: |
|
print(colorText.BOLD + colorText.FAIL + |
|
'[+] Failed to save user config. Exiting..' + colorText.END) |
|
input('') |
|
sys.exit(1) |
|
|
|
|
|
def getConfig(self, parser): |
|
if len(parser.read('screenipy.ini')): |
|
try: |
|
self.duration = parser.get('config', 'duration') |
|
self.period = parser.get('config', 'period') |
|
self.minLTP = float(parser.get('config', 'minprice')) |
|
self.maxLTP = float(parser.get('config', 'maxprice')) |
|
self.volumeRatio = float(parser.get('config', 'volumeRatio')) |
|
self.consolidationPercentage = float( |
|
parser.get('config', 'consolidationPercentage')) |
|
self.daysToLookback = int( |
|
parser.get('config', 'daysToLookback')) |
|
if 'n' not in str(parser.get('config', 'shuffle')).lower(): |
|
self.shuffleEnabled = True |
|
if 'n' not in str(parser.get('config', 'cachestockdata')).lower(): |
|
self.cacheEnabled = True |
|
if 'n' not in str(parser.get('config', 'onlyStageTwoStocks')).lower(): |
|
self.stageTwo = True |
|
else: |
|
self.stageTwo = False |
|
if 'y' not in str(parser.get('config', 'useEMA')).lower(): |
|
self.useEMA = False |
|
else: |
|
self.useEMA = True |
|
except configparser.NoOptionError: |
|
input(colorText.BOLD + colorText.FAIL + |
|
'[+] Screenipy requires user configuration again. Press enter to continue..' + colorText.END) |
|
parser.remove_section('config') |
|
self.setConfig(parser, default=False) |
|
else: |
|
self.setConfig(parser, default=True) |
|
|
|
|
|
def showConfigFile(self): |
|
try: |
|
f = open('screenipy.ini', 'r') |
|
print(colorText.BOLD + colorText.GREEN + |
|
'[+] Screeni-py User Configuration:' + colorText.END) |
|
print("\n"+f.read()) |
|
f.close() |
|
input('') |
|
except: |
|
print(colorText.BOLD + colorText.FAIL + |
|
"[+] User Configuration not found!" + colorText.END) |
|
print(colorText.BOLD + colorText.WARN + |
|
"[+] Configure the limits to continue." + colorText.END) |
|
self.setConfig(parser) |
|
|
|
|
|
def checkConfigFile(self): |
|
try: |
|
f = open('screenipy.ini','r') |
|
f.close() |
|
return True |
|
except FileNotFoundError: |
|
return False |
|
|
|
|
|
def getPeriodNumeric(self): |
|
import re |
|
pattern = re.compile(r'\d+') |
|
result = [int(match.group()) for match in pattern.finditer(self.period)][0] |
|
return result |
|
|
|
|