def setupForTheTradeObject(self, getMax=False, infile="trades.8.csv"): '''Set up the DataFrames''' jf = JournalFiles(mydevel=True, infile=infile, indir="data/", outdir="out/") tkt = Statement_DAS(jf) trades, jf = tkt.getTrades() idf = InputDataFrame() trades, success = idf.processInputFile(trades) tu = DefineTrades() (dummy_len, dummy_df, ldf) = tu.processOutputDframe(trades) srf = SumReqFields() maxlen = 0 maxindex = -1 #The index that yieds the maximumbal from ldf[i] for i in range(len(ldf)): if len(ldf[i]) > maxlen: maxlen = len(ldf[i]) maxindex = i index = randint(0, len(ldf) - 1) if getMax: index = maxindex self.atrade = ldf[index] self.tto = TheTradeObject(self.atrade, True, srf) return self.tto
def reloadit(self): from journal.statement import Statement_DAS as Ticket from journal.statement import Statement_IBActivity from journal.pandasutil import InputDataFrame infile = self.jf.inpathfile if not os.path.exists(infile): print("There is a problem. Unable to fully save this file.") return None if self.jf.inputType == 'IB_HTML': statement = Statement_IBActivity(self.jf) df = statement.getTrades_IBActivity(self.jf.inpathfile) elif self.jf.inputType == 'DAS': tkt = Ticket(self.jf) df, self.jf = tkt.getTrades() # trades = pd.read_csv(self.jf.inpathfile) else: #Temporary print('Opening a non standard file name in DAS') tkt = Ticket(self.jf) df, self.jf = tkt.getTrades() idf = InputDataFrame() trades, success = idf.processInputFile(df, self.jf.theDate, self.jf) self.df = trades if not success: return
def test_NewSingleTxPerTicket(self): ''' Test the method Statement_DAS.newSingleTxPerTicket. That method creates a new csv file reducing multi row transactions to a single row, averaging the prices, totaling the amounts. Explicitly tests: A newFile has been created and made the infile of JournalFiles. The PL summary is the same between the two files . The shares total for each symbol/account/buy/sell is the same ''' rc = ReqCol() for infile in self.infiles: outdir = 'out/' indir = 'data/' indir = os.path.realpath(indir) jf = JournalFiles(indir=indir, infile=infile, outdir=outdir) origdframe = pd.read_csv(jf.inpathfile) originfile = jf.infile tkt = Statement_DAS(jf) newDF, jf = tkt.getTrades() self.assertNotEqual(originfile, jf.infile) newdframe = pd.read_csv(jf.inpathfile) self.assertAlmostEqual(origdframe[rc.PL].sum(), newdframe[rc.PL].sum(), places=10) self.assertAlmostEqual(newDF[rc.PL].sum(), newdframe[rc.PL].sum(), places=10) for symbol in origdframe[rc.ticker].unique(): for accnt in origdframe[rc.acct].unique(): d = origdframe n = newDF d = d[d[rc.ticker] == symbol] d = d[d[rc.acct] == accnt] dbuy = d[d[rc.side].str.startswith('B')] dsell = d[d[rc.side].str.startswith('S')] n = n[n[rc.ticker] == symbol] n = n[n[rc.acct] == accnt] nbuy = n[n[rc.side].str.startswith('B')] nsell = n[n[rc.side].str.startswith('S')] self.assertEqual(dbuy[rc.shares].sum(), nbuy[rc.shares].sum()) self.assertEqual(dsell[rc.shares].sum(), nsell[rc.shares].sum())
def test_CreateSingleTicket(self): ''' Test the method Statement_DAS.createSingleTicket. Requires the list of dfs created by getListOfTicketDF. Explicitly test that each element is a 1 row DataFrame. That the new price, (the average price of its transactions) is always greater than the min and less than the max. And finally check that the total number of shares (total) is the same as the sum of shares in constituent transactions. ''' rc = ReqCol() indir = 'data/' outdir = 'out/' infiles = self.infiles for infile in infiles: jf = JournalFiles(indir=indir, infile=infile, outdir=outdir) tkt = Statement_DAS(jf) listTick = tkt.getListOfTicketDF() totalSharesForDay = 0 for tick in listTick: singleTicket = tkt.createSingleTicket(tick) self.assertIsInstance(singleTicket, type(pd.DataFrame()), "Failed to create a DataFrame") self.assertEqual(len(singleTicket), 1, "Failed to create a single item ticket") # print(tick[rc.price].min()) # print(singleTicket[rc.price].unique()[0]) # print(tick[rc.price].max()) # print() try: isclose(singleTicket[rc.price].unique()[0], tick[rc.price].max(), abs_tol=1e-8) except AssertionError: self.assertLessEqual(singleTicket[rc.price].unique()[0], tick[rc.price].max()) try: isclose(singleTicket[rc.price].unique()[0], tick[rc.price].min(), abs_tol=1e-8) except AssertionError: self.assertGreaterEqual(singleTicket[rc.price].unique()[0], tick[rc.price].min()) totalSharesForDay = totalSharesForDay + tick[rc.shares].sum() dframe = pd.read_csv(jf.inpathfile) self.assertEqual( dframe[rc.shares].sum(), totalSharesForDay, "Failed to acount for all the shares transacted.")
def getPositions(self, jf): ''' Open the positions csv. It is either a DAS export or a file created to the same specs. Currently, this file is only used when using a DAS export from the trades window and is only necessary if any trades in the input file have balance trades before or after. :params jf: The JournalFiles object. It may be None and the variable for the location at jf.inpathfile2 may also be None. :return: A DataFrame. If no results were retrieved, return an empty DataFrame. :TODO: getPositions should be a virtual method--not sure how to implement it best This aint java. For now just kludge solidly. ''' if jf and jf.inputType == JournalFiles.inputType[ 'das'] and jf.inpathfile2: st = Statement_DAS(jf) df = st.getPositions() return df elif jf and jf.inputType == JournalFiles.inputType['ib']: st = Statement_IBActivity(jf) df = st.getPositions() return df return pd.DataFrame()
def test_GetListOfTicketDF(self): ''' Test the method Statement_DAS.getListOfTicketDF. Explicitly tests: Each ticket has only long or short only Each ticket has a single ticker symbol, cloid, and account ''' rc = ReqCol() outdir = 'data/' # A list of files that were problematic infiles = self.infiles # otherinfiles = ['trades.911.noPL.csv', 'trades.csv'] for f in infiles: # trade = os.path.join(outdir, f) jf = JournalFiles(indir=outdir, infile=f, outdir='out/', mydevel=True) tkt = Statement_DAS(jf) tktList = tkt.getListOfTicketDF() totalTX = 0 for ticket in tktList: self.assertEqual( len(ticket[rc.side].unique()), 1, "There can only be one side, long or short, in a ticket") self.assertEqual(len(ticket[rc.ticker].unique()), 1, "There can only be one ticker in a ticket") self.assertEqual(len(ticket['Cloid'].unique()), 1, "There can be only one Cloid in a ticket") self.assertEqual(len(ticket[rc.acct].unique()), 1, "There can be only one account in a ticket") totalTX = totalTX + len(ticket) trades = pd.read_csv(jf.inpathfile) msg = "There is a discrepancy in number of transactions in the tickets" self.assertEqual(len(trades), totalTX, msg)
def runnit(self): print('gonna runnit gonna runnit') self.initialize() if not self.indir: print('What file is supposed 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 == 'IB_HTML': jf.inputType = 'IB_HTML' statement = Statement_IBActivity(jf) df = statement.getTrades_IBActivity(jf.inpathfile) elif self.inputtype == 'DAS': tkt = Ticket(jf) df, jf = tkt.getTrades() # trades = pd.read_csv(jf.inpathfile) else: #Temporary print('Opening a non standard file name in DAS') tkt = Ticket(jf) df, jf = tkt.getTrades() idf = InputDataFrame() trades, success = idf.processInputFile(df, jf.theDate, jf) if not success: return tu = DefineTrades(self.inputtype) inputlen, dframe, ldf = tu.processOutputDframe(trades) # Process the openpyxl excel object using the output file DataFrame. Insert # images and Trade Summaries. margin = 25 lf = LayoutForms(self.sc, jf, dframe) tradeSummaries = lf.runSummaries(ldf)
def test_createImageLocation(self, unusedstub): '''Run structjour''' #Assert the initial entry location in imageLocation is figured by these factors # Assert all entries are figured by summarySize and len of the minittrade # Assert the minitable locations are offset by length of the minitrade tables + # ls.spacing, and the first entry in the Leftmost column starts with 'Trade' # Assert third entry of imageLocation begins with 'Trade' and contains # ['Long', 'Short'] # Assert the 4th entry is a timestamp # Assert the 5th entry is a time delta global D for tdata, infile in zip(self.dadata, self.infiles): # ::::::::: Setup :::::::: D = deque(tdata) # infile = 'trades.8.csv' print(infile) indir = 'data/' mydevel = False jf = JournalFiles(indir=indir, infile=infile, mydevel=mydevel) trades, jf = Statement_DAS(jf).getTrades() trades, success = InputDataFrame().processInputFile(trades) inputlen, dframe, ldf = DefineTrades().processOutputDframe(trades) # ::::::::::: end setup ::::::::::::: margin = 25 spacing = 3 ls = LayoutSheet(margin, inputlen, spacing=spacing) imageLocation, dframe = ls.imageData(dframe, ldf) for count, t in enumerate(imageLocation): if count == 0: initialEntry = ls.inputlen + ls.topMargin + ls.spacing + len(ldf[0]) + 2 self.assertEqual(t[0], initialEntry) else: nextloc = imageLocation[count-1][0] + len(ldf[count]) + ls.summarySize self.assertEqual(t[0], nextloc) t_entry = t[0] - (spacing + len(ldf[count])) self.assertTrue(dframe.iloc[t_entry][0].startswith('Trade')) self.assertEqual(len(dframe.iloc[t_entry-1][0]), 0) self.assertTrue(t[2].startswith('Trade')) self.assertTrue(t[2].find('Long') > 0 or t[2].find('Short') > 0) self.assertTrue(isinstance(pd.Timestamp('2019-11-11 ' + t[3]), dt.datetime)) self.assertTrue(isinstance(t[4], dt.timedelta))
def test_runSummaries(self, unusedstub1, unusedstub2, unusedstub3): ''' Test the method prunSummaries. The setup here is alost the entire module trade.py We run the standard set of infiles ''' # global D # infiles = ['trades.1116_messedUpTradeSummary10.csv', 'trades.8.WithHolds.csv', # 'trades.8.csv', 'trades.907.WithChangingHolds.csv', # 'trades_190117_HoldError.csv', 'trades.8.ExcelEdited.csv', # 'trades.910.tickets.csv', 'trades_tuesday_1121_DivBy0_bug.csv', # 'trades.8.WithBothHolds.csv', 'trades1105HoldShortEnd.csv', # 'trades190221.BHoldPreExit.csv'] global D for tdata, infile in zip(self.dadata, self.infiles): # ::::::::: Setup :::::::: D = deque(tdata) # :::::::::::::: SETUP :::::::::::::: # theDate = '2018-11-05' outdir = 'out/' indir = 'C:/python/E/structjour/src/data/' mydevel = False jf = JournalFiles(infile=infile, outdir=outdir, indir=indir, mydevel=mydevel) trades, jf = Statement_DAS(jf).getTrades() trades, success = InputDataFrame().processInputFile(trades) inputlen, dframe, ldf = DefineTrades().processOutputDframe(trades) # Process the openpyxl excel object using the output file DataFrame. Insert # images and Trade Summaries. margin = 25 # Create the space in dframe to add the summary information for each trade. # Then create the Workbook. ls = LayoutSheet(margin, inputlen) imageLocation, dframe = ls.imageData(dframe, ldf) wb, ws, dummy = ls.createWorkbook(dframe) tf = TradeFormat(wb) mstkAnchor = (len(dframe.columns) + 2, 1) mistake = MistakeSummary(numTrades=len(ldf), anchor=mstkAnchor) mistake.mstkSumStyle(ws, tf, mstkAnchor) # :::::::::::::: END SETUP :::::::::::::: tradeSummaries = ls.runSummaries(imageLocation, ldf, jf, ws, tf) # Make sure the out dir exists if not os.path.exists("out/"): os.mkdir("out/") # Make sure the file we are about to create does not exist dispath = "out/SCHNOrK.xlsx" if os.path.exists(dispath): os.remove(dispath) wb.save(dispath) wb2 = load_workbook(dispath) ws2 = wb2.active srf = SumReqFields() for trade, loc in zip(tradeSummaries, imageLocation): anchor = (1, loc[0]) # print(anchor) for col in trade: if col in['clean']: continue # Get the cell if isinstance(srf.tfcolumns[col][0], list): cell = tcell(srf.tfcolumns[col][0][0], anchor=anchor) else: cell = tcell(srf.tfcolumns[col][0], anchor=anchor) # Nicer to read wsval = ws2[cell].value tval = trade[col].unique()[0] # Test Formulas (mostly skipping for now because its gnarly) # Formulas in srf.tfformulas including the translation stuff if col in srf.tfformulas.keys(): self.assertTrue(wsval.startswith('=')) # Test empty cells elif not tval: # print(wsval, '<------->', tval) self.assertIs(wsval, None) # Test floats elif isinstance(tval, float): # print(wsval, '<------->', tval) self.assertAlmostEqual(wsval, tval) # Time vals elif isinstance(tval, (pd.Timestamp, dt.datetime, np.datetime64)): wsval = pd.Timestamp(wsval) tval = pd.Timestamp(tval) self.assertGreaterEqual((wsval-tval).total_seconds(), -.01) self.assertLessEqual((wsval-tval).total_seconds(), .01) # Test everything else else: # print(wsval, '<------->', tval) self.assertEqual(wsval, tval)
def test_populateDailySummaryForm(self, unusedstub1, unusedstub2, unusedstub3): ''' Test the method populateMistakeForm. The setup here is alost the entire module trade.py The tested method puts in the trade PL and notes ''' global D for tdata, infile in zip(self.dadata, self.infiles): # ::::::::: Setup :::::::: D = deque(tdata) # :::::::::::::: SETUP :::::::::::::: # theDate = '2018-11-05' outdir = 'out/' indir = 'data/' mydevel = False jf = JournalFiles(infile=infile, outdir=outdir, indir=indir, mydevel=mydevel) print(jf.inpathfile) trades, jf = Statement_DAS(jf).getTrades() trades, success = InputDataFrame().processInputFile(trades) inputlen, dframe, ldf = DefineTrades().processOutputDframe(trades) # Process the openpyxl excel object using the output file DataFrame. Insert # images and Trade Summaries. margin = 25 # Create the space in dframe to add the summary information for each trade. # Then create the Workbook. ls = LayoutSheet(margin, inputlen) imageLocation, dframe = ls.imageData(dframe, ldf) wb, ws, dummy = ls.createWorkbook(dframe) tf = TradeFormat(wb) mstkAnchor = (len(dframe.columns) + 2, 1) mistake = MistakeSummary(numTrades=len(ldf), anchor=mstkAnchor) # mistake.mstkSumStyle(ws, tf, mstkAnchor) mistake.dailySumStyle(ws, tf, mstkAnchor) tradeSummaries = ls.runSummaries(imageLocation, ldf, jf, ws, tf) # :::::::::::::: END SETUP :::::::::::::: # ls.populateMistakeForm(tradeSummaries, mistake, ws, imageLocation) ls.populateDailySummaryForm(tradeSummaries, mistake, ws, mstkAnchor) # Make sure the out dir exists if not os.path.exists("out/"): os.mkdir("out/") # Make sure the file we are about to create does not exist dispath = "out/SCHNOrK.xlsx" if os.path.exists(dispath): os.remove(dispath) wb.save(dispath) # print(infile, 'saved as', dispath) wb2 = load_workbook(dispath) ws2 = wb2.active # Live Total frc = FinReqCol() livetot = 0 simtot = 0 highest = 0 lowest = 0 numwins = 0 numlosses = 0 totwins = 0 totloss = 0 for trade in tradeSummaries: acct = trade[frc.acct].unique()[0] pl = trade[frc.PL].unique()[0] highest = pl if pl > highest else highest lowest = pl if pl < lowest else lowest if pl > 0: numwins += 1 totwins += pl # Trades == 0 are figured in the loss column-- comissions and all else: numlosses += 1 totloss += pl if acct == 'Live': livetot += pl elif acct == 'SIM': simtot += pl # print(livetot) # livetotcell = tcell(mistake.dailySummaryFields['livetot'][0], anchor=ls.DSFAnchor) # print(simtot) avgwin = 0 if numwins == 0 else totwins/numwins avgloss = 0 if numlosses == 0 else totloss/numlosses data = [['livetot', livetot], ['simtot', simtot], ['highest', highest], ['lowest', lowest], ['avgwin', avgwin], ['avgloss', avgloss]] for s, d in data: cell = tcell(mistake.dailySummaryFields[s][0], anchor=ls.DSFAnchor) # msg = '{} {} {}'.format(s, d, ws2[cell].value) # print(msg) self.assertAlmostEqual(d, ws2[cell].value) #, abs_tol=1e-7) data = ['livetotnote', 'simtotnote', 'highestnote', 'lowestnote', 'avgwinnote', 'avglossnote'] for s in data: cell = tcell(mistake.dailySummaryFields[s][0][0], anchor=ls.DSFAnchor) val = ws2[cell].value self.assertIsInstance(val, str) self.assertGreater(len(val), 1)
def test_populateMistakeForm(self, unusedstub1, unusedstub2, unusedstub3): ''' Test the method populateMistakeForm. The setup here is alost the entire module trade.py ''' global D for tdata, infile in zip(self.dadata, self.infiles): # ::::::::: Setup :::::::: D = deque(tdata) # :::::::::::::: SETUP :::::::::::::: # theDate = '2018-11-05' outdir = 'out/' indir = 'data/' mydevel = False jf = JournalFiles(infile=infile, outdir=outdir, indir=indir, mydevel=mydevel) trades, jf = Statement_DAS(jf).getTrades() trades, success = InputDataFrame().processInputFile(trades) inputlen, dframe, ldf = DefineTrades().processOutputDframe(trades) # Process the openpyxl excel object using the output file DataFrame. Insert # images and Trade Summaries. margin = 25 # Create the space in dframe to add the summary information for each trade. # Then create the Workbook. ls = LayoutSheet(margin, inputlen) imageLocation, dframe = ls.imageData(dframe, ldf) wb, ws, dummy = ls.createWorkbook(dframe) tf = TradeFormat(wb) mstkAnchor = (len(dframe.columns) + 2, 1) mistake = MistakeSummary(numTrades=len(ldf), anchor=mstkAnchor) mistake.mstkSumStyle(ws, tf, mstkAnchor) tradeSummaries = ls.runSummaries(imageLocation, ldf, jf, ws, tf) # :::::::::::::: END SETUP :::::::::::::: ls.populateMistakeForm(tradeSummaries, mistake, ws, imageLocation) # Make sure the out dir exists if not os.path.exists("out/"): os.mkdir("out/") # Make sure the file we are about to create does not exist dispath = "out/SCHNOrK.xlsx" if os.path.exists(dispath): os.remove(dispath) wb.save(dispath) wb2 = load_workbook(dispath) ws2 = wb2.active frc = FinReqCol() # ragged iteration over mistakeFields and tradeSummaries. count = 0 # ragged iterator for tradeSummaries for key in mistake.mistakeFields: entry = mistake.mistakeFields[key] cell = entry[0][0] if isinstance(entry[0], list) else entry[0] cell = tcell(cell, anchor=mistake.anchor) if key.startswith('name'): # Get the hyperlink target in mistakeform , parse the target and verify the # hyperlinks point to each other tsName = tradeSummaries[count][frc.name].unique()[0] tsAcct = tradeSummaries[count][frc.acct].unique()[0] targetcell = ws2[cell].hyperlink.target.split('!')[1] originalcell = ws2[targetcell].hyperlink.target.split('!')[1] # print(ws2[cell].value, '<--------', tsumName) # print(ws2[cell].value, '<-------', tsumAccount) # print(cell, '<------->', originalcell) self.assertGreater(ws2[cell].value.find(tsName), -1) self.assertGreater(ws2[cell].value.find(tsAcct), -1) self.assertEqual(cell, originalcell) count = count + 1 # ::::::: tpl fields ::::::: count = 0 for key in mistake.mistakeFields: entry = mistake.mistakeFields[key] cell = entry[0][0] if isinstance(entry[0], list) else entry[0] cell = tcell(cell, anchor=mistake.anchor) if key.startswith('tpl'): targetcell = ws2[cell].value[1:] origval = tradeSummaries[count][frc.PL].unique()[0] # print(ws2[targetcell].value, '<------->', origval ) if origval == 0: self.assertIs(ws2[targetcell].value, None) else: self.assertAlmostEqual(ws2[targetcell].value, origval) count = count + 1 # These next two tests (for plx and mistakex) have no unique entries (without user # input or mock) Test for the static values and that plx entry is next to its header if key.startswith('pl'): headval = 'Proceeds Lost' targetcell = ws2[cell].value[1:] headercell = 'A' + targetcell[1:] # print(ws2[targetcell].value, '<------->', None) # print(headercell, '------->', ws2[headercell].value) self.assertTrue(ws2[targetcell].value is None) self.assertEqual(ws2[headercell].value, headval) if key.startswith('mistake'): noteval = 'Final note' targetcell = ws2[cell].value[1:] # print(ws2[targetcell].value, '<------->', noteval) self.assertEqual(ws2[targetcell].value, noteval)
def run(infile='trades.csv', outdir=None, theDate=None, indir=None, infile2=None, mydevel=True): ''' Run structjour. Temporary picker for input type based on filename. If infile has 'activity' in it and ends in .html, then its IB Activity Statement web page (as a file on this system) :params infile: Name of the input file. Default trades.csv--a DAS export from the trades window. If infile contains the string 'trades', input type is set to DAS. If infile contains the string 'activity', input type is set to IB Activity. Default will try DAS :params outdir: Location to write the output file. :params theDate: The date of this input file. If trades lack a Date, this date be the trade date. :params indir: Location of the input file. :parmas infile2: Name of the DAS positions file. Will default to indir/positions.csv :params mydevel: If True, use a specific file structure and let structjour create it. All can be overriden by using the specific parameters above. ''' settings = QSettings('zero_substance', 'structjour') settings.setValue('runType', 'CONSOLE') # indir=None, outdir=None, theDate=None, infile='trades.csv', mydevel=False jf = JournalFiles(indir=indir, outdir=outdir, theDate=theDate, infile=infile, infile2=infile2, mydevel=mydevel) name, ext = os.path.splitext(jf.infile.lower()) if name.find('activity') > -1 and ext == '.html': jf.inputType = 'IB_HTML' statement = Statement_IBActivity(jf) df = statement.getTrades_IBActivity(jf.inpathfile) elif name.find('trades') > -1 and ext == '.csv': # This could be an IB CSV--so this is temporary-- when I enable some sort of IB CSV, will # probably do some kind of class heirarchy here for statements. tkt = Ticket(jf) df, jf = tkt.getTrades() # trades = pd.read_csv(jf.inpathfile) else: #Temporary print('Opening a non standard file name in DAS') tkt = Ticket(jf) df, jf = tkt.getTrades() idf = InputDataFrame() trades, success = idf.processInputFile(df, jf.theDate, jf) if not success: print('Failed. Between you and me, I think its a programming error') return jf tu = DefineTrades() inputlen, dframe, ldf = tu.processOutputDframe(trades) # Process the openpyxl excel object using the output file DataFrame. Insert # images and Trade Summaries. margin = 25 # Create the space in dframe to add the summary information for each trade. # Then create the Workbook. ls = LayoutSheet(margin, inputlen) imageLocation, dframe = ls.imageData(dframe, ldf) wb, ws, nt = ls.createWorkbook(dframe) tf = TradeFormat(wb) ls.styleTop(ws, len(nt.columns), tf) assert len(ldf) == len(imageLocation) mstkAnchor = (len(dframe.columns) + 2, 1) mistake = MistakeSummary(numTrades=len(ldf), anchor=mstkAnchor) mistake.mstkSumStyle(ws, tf, mstkAnchor) mistake.dailySumStyle(ws, tf, mstkAnchor) tradeSummaries = ls.runSummaries(imageLocation, ldf, jf, ws, tf) # app = QApplication(sys.argv) # qtf = QtForm() # qtf.fillForm(tradeSummaries[1]) # app.exec_() ls.populateMistakeForm(tradeSummaries, mistake, ws, imageLocation) ls.populateDailySummaryForm(tradeSummaries, mistake, ws, mstkAnchor) ls.save(wb, jf) print("Processing complete. Saved {}".format(jf.outpathfile)) return jf