class runController: ''' Programming notes-- minimize the use of the ui (self.ui). Instead create high level interface in sc as needed. :Settings-keys: ['theDate', 'setToday', scheme', 'journal', 'dasInfile', 'dasInfile2', 'ibInfile', outdir, 'interval', inputType] ''' def __init__(self, sc): self.sc = sc self.ui = self.sc.ui self.initialize() self.inputtype = None self.ui.goBtn.pressed.connect(self.runnit) self.sc.ui.dateEdit.dateChanged.connect(self.theDateChanged) # self.ui.loadBtn.pressed.connect(self.loadit) self.loadedDate = None self.statement = None def theDateChanged(self, val): self.sc.dateInSync = False self.sc.theDateChanged(val) if self.sc.ui.useDatabase.isChecked() and self.gotTrades(): self.runnit() def initialize(self): ''' Initialize the inputs and outs ''' # Might blitz thes lines if JournalFiles gets an overhaul. For ease of transaiton # We keep JournalFiles till its allworks into the Qt run self.settings = self.sc.settings self.inputtype = self.settings.value('inputType') self.indir = self.sc.getDirectory() inkey = '' if self.inputtype == 'DAS': inkey = 'dasInfile' elif self.inputtype == 'IB_HTML': inkey = 'ibInfileName' if self.settings.value('outdirPolicy') == 'default': self.outdir = None else: self.outdir = self.settings.value('outdir') theDate = self.settings.value('theDate', pd.Timestamp.today()) if theDate and isinstance(theDate, (QDate, QDateTime)): theDate = qtime2pd(theDate) self.theDate = theDate self.positions = self.settings.value('dasInfile2') # end blitz self.infile = self.settings.value(inkey) self.inpathfile = self.ui.infileEdit.text() if os.path.splitext(self.inpathfile)[1].lower() == ".csv": self.infile = os.path.split(self.inpathfile)[1] self.sc.setWindowTitle(self.sc.baseWindowTitle) def loadit(self): ''' Load saved objects ''' self.sc.doWeSave() daDate = self.ui.dateEdit.date() daDate = qtime2pd(daDate) self.loadedDate = daDate self.settings.setValue('theDate', daDate) self.initialize() if not self.indir: logging.info('No file to load?') return jf = JournalFiles(indir=self.indir, outdir=self.outdir, theDate=self.theDate, infile=self.infile, inputType=self.inputtype, infile2=self.positions, mydevel=True) lf = LayoutForms(self.sc, jf, None) if not lf.loadTradesFromDB(daDate): msg = f'No user data has been saved for {daDate.strftime("%A %B %d")}. Loading trade data.' logging.info(msg) # msgbx = QMessageBox() # msgbx.setIconPixmap(QPixmap("structjour/images/ZSLogo.png")) # msgbx.setText(msg) # msgbx.exec() self.runnit(True) def runDBInput(self, daDate, jf): ''' Get the trades from daDate in the DB and process the trades ''' self.statement = StatementDB() daDate = qtime2pd(daDate) df = self.statement.getStatement(daDate) if df.empty: return False tu = DefineTrades(self.inputtype) dframe, ldf = tu.processDBTrades(df) lf = LayoutForms(self.sc, jf, dframe) lf.pickleitnow() lf.runTtoSummaries(ldf) self.statement.addTradeSummaries(lf.ts, ldf) return True def gotTrades(self): ''' From the text in the infileEdit box, determine if we have trades in the db. We can tell because the first token is an int showing how many trades are held. ''' if self.sc.ui.useDatabase.isChecked(): text = self.sc.ui.infileEdit.text() if len(text): try: num = int(text.split(' ')[0]) if num > 0: return True except Exception: pass if self.statement is not None: count, countt = self.statement.getNumTicketsForDay( qtime2pd(self.sc.ui.dateEdit.date())) if count > 0 or countt > 0: return True return False def runnit(self, loaditrun=False): ''' Load an initial input file and process it. ''' self.sc.dateInSync = True if self.sc.ui.useDatabase.isChecked() and loaditrun is False: if not self.gotTrades(): return return self.loadit() self.sc.doWeSave() self.initialize() if not self.indir: logging.info('No file to load?') return jf = JournalFiles(indir=self.indir, outdir=self.outdir, theDate=self.theDate, infile=self.infile, inputType=self.inputtype, infile2=self.positions, mydevel=True) if self.inputtype == 'DB': self.runDBInput(self.theDate, jf) windowTitle = f'{self.sc.baseWindowTitle}: {self.sc.ui.infileEdit.text()}: no user data' self.sc.setWindowTitle(windowTitle) if self.gotTrades(): self.sc.ui.useDatabase.setChecked(True) self.sc.dbDefault(True) return local = os.path.normpath(self.ui.infileEdit.text()) if os.path.normpath(jf.inpathfile) != local: if os.path.exists(local): d, jf.infile = os.path.split(local) jf.inpathfile = local x, inputType = getStatementType(jf.inpathfile) if not inputType: msg = f'<h3>No trades found. File does not appear to be a statement</h3><ul> ' msg += f'<div><strong>{jf.inpathfile}</strong></div>' msgbx = QMessageBox() msgbx.setIconPixmap(QPixmap("structjour/images/ZSLogo.png")) msgbx.setText(msg) msgbx.exec() return self.inputtype = inputType windowTitle = self.sc.baseWindowTitle + ': ' + self.sc.ui.infileEdit.text( ) + ': no user data' self.sc.setWindowTitle(windowTitle) if self.inputtype == 'IB_HTML' or self.inputtype == 'IB_CSV': jf.inputType = self.inputtype ibs = IbStatement() x = ibs.openIBStatement(jf.inpathfile) msg = '' if x[0]: tkey = 'Trades' if 'Trades' in x[0].keys( ) else 'TRNT' if 'TRNT' in x[0].keys() else None if not tkey: raise ValueError( f'Error in processing statemnt {jf.inpathfile}') numtickets = len(x[0][tkey]) gotToday = self.runDBInput(self.theDate, jf) if gotToday: if self.gotTrades(): self.sc.ui.useDatabase.setChecked(True) self.sc.dbDefault(True) else: msg = f'<h3>No trades found on date {self.theDate.date()}</h3><ul> ' msg += f'<div><strong>{jf.inpathfile}</strong></div>' msg += f'<div>Found {numtickets} tickets. They are now in DB</div>' msg += f'<div>{list(x[1].keys())}</div>' else: msg = f'<h3>No trades recorded from the file:</h3><ul> ' msg = msg + f'<div><strong>{jf.inpathfile}</strong></div>' msg = msg + f'<div>{x[1]}</div>' msgbx = QMessageBox() msgbx.setIconPixmap(QPixmap("structjour/images/ZSLogo.png")) msgbx.setText(msg) msgbx.exec() return elif self.inputtype == 'DAS': x = checkDateDir(jf.inpathfile) if not x: msg = "<h3>The date for this DAS statement is not clear</h3>" msg += "<div>Please enter the date for this statement</div>" msg += f'<div><strong>{jf.inpathfile}</strong></div>' msg += '<div>(YYYYMMDD) ex: 20190113</div>' theDate = getDate(msg) if theDate: self.settings.setValue('theDate', theDate) self.sc.ui.dateEdit.setDate(pd2qtime(theDate, qdate=True)) else: return ds = DasStatement(jf.infile, self.settings, self.theDate) ds.getTrades() self.runDBInput(self.theDate, jf) if self.gotTrades(): self.sc.ui.useDatabase.setChecked(True) self.sc.dbDefault(True) return else: msg = '<h3>Unrecognized input:</h3><ul> ' msgbx = QMessageBox() msgbx.setIconPixmap(QPixmap("structjour/images/ZSLogo.png")) msgbx.setText(msg) msgbx.exec() return
def registerTrades(wb, theDate): ibdb = StatementDB() df = ibdb.getStatement(theDate) x = ibdb.getNumTicketsForDay(theDate) if not x[0] or not x[1]: msg = f'''found {x[0]} tickets and {x[1]} trades. Nothing to process for the date {theDate.strftime('%A %d, %Y')}in the DB''' return msg tu = DefineTrades('DB') df, ldf = tu.processDBTrades(df) ts, entries = ibdb.getTradeSummaries(theDate) # self.ts = setTradeSummaryHeaders(ts) drc = DisReqCol(theDate) tlog = wb["Trade Log"] account = tlog['I1'].value startSearch = False ix = -2 cols = drc.tfcolumns # Here is a list of the keys to use cols.keys() of the trade log DataFrame # ['date', 'time', 'side', 'symb', 'entry1', 'acctbal', 'shares', # 'stoploss', 'targ', 'avgexit', 'pl', 'notes']) # Not bothering with the abstraction (drc.name) because this is entirely ours. srf = SumReqFields() ids = list() for row in tlog.iter_rows(): # Unconventional loop stuff anchor = (1, row[0].row) keys = list(ts.keys()) if startSearch is True: idcol = cols['id'][0][0] - 1 if row[idcol].value: ids.append(row[idcol].value) if not row[0].value: startSearch = False ix = 0 if row[0].value == 'Date': startSearch = True if ix >= 0 and ix < len(keys): while True and ix < len(keys): key = keys[ix] tdf = ts[key] if not gotAnyExits(tdf) or tdf['id'].unique()[0] in ids or ( tdf['Account'].unique()[0] != account): if tdf['Account'].unique()[0] not in ['SIM', 'Live']: raise ValueError('Programmer exception in search of weird data') # Continue the inner loop -- keep row from the outer loop loop ix += 1 continue # date cell = tcell(cols['date'][0], anchor=anchor) tlog[cell] = theDate # time cell = tcell(cols['time'][0], anchor=anchor) tim = tdf[srf.start].unique()[0] tlog[cell] = tim # side name = tdf[srf.name].unique()[0] if name: cell = tcell(cols['side'][0], anchor=anchor) tlog[cell] = name.split()[1] # symb cell = tcell(cols['symb'][0], anchor=anchor) tlog[cell] = name.split()[0] # entry1 cell = tcell(cols['entry1'][0], anchor=anchor) tlog[cell] = tdf[srf.entry1].unique()[0] # Account Balance (setting an excel formula) cell = tcell(cols['acctbal'][0], anchor=anchor) formula = "=$M$3+SUM($N$7:$N{})".format(row[0].row - 1) tlog[cell] = formula # "shares" cell = tcell(cols['shares'][0], anchor=anchor) shares = tdf[srf.shares].unique()[0].split()[0] if len(shares) > 0: try: ishares = int(shares) except ValueError: ishares = 0 tlog[cell] = ishares # stoploss cell = tcell(cols['stoploss'][0], anchor=anchor) sl = tdf[srf.stoploss].unique()[0] if isinstance(sl, bytes): sl = None tlog[cell] = sl # target cell = tcell(cols['targ'][0], anchor=anchor) target = tdf[srf.targ].unique()[0] if isinstance(target, bytes): target = None tlog[cell] = target # avgExit cell = tcell(cols['avgexit'][0], anchor=anchor) tlog[cell] = getAvgExit(tdf) # P/L cell = tcell(cols['pl'][0], anchor=anchor) pl = tdf[srf.pl].unique()[0] # Don't know how these are sneaking through-- Is it only on legacy files? if isinstance(pl, bytes): pl = 0.0 tlog[cell] = pl # Strategy cell = tcell(cols['strat'][0], anchor=anchor) strat = tdf[srf.strat].unique()[0] tlog[cell] = strat # notes (from the mistake note field) cell = tcell(cols['notes'][0], anchor=anchor) note = tdf[srf.mstknote].unique()[0] tlog[cell] = note # id (from the database) cell = tcell(cols['id'][0], anchor=anchor) id = tdf['id'].unique()[0] tlog[cell] = id # break the inner loop break ix += 1 if ix >= len(keys): # done with outer loop break return None