def addStartTimeDB(self, dframe): ''' Add the start time to the new column labeled Start or frc.start. Each transaction whthin a trade will share a start time. :params dframe: The output df to place the data :return dframe: The same dframe but with the new start data. ''' rc = self._frc newTrade = True oldsymb = '' oldaccnt = '' for i, row in dframe.iterrows(): if row[rc.ticker] != oldsymb or row[rc.acct] != oldaccnt or row[ rc.shares] == row[rc.bal]: newTrade = True oldsymb = row[rc.ticker] oldaccnt = row[rc.acct] if newTrade: newTrade = True if isNumeric( row[rc.bal]) and row[rc.bal] == 0 else False oldTime = dframe.at[i, rc.time] dframe.at[i, rc.start] = oldTime else: dframe.at[i, rc.start] = oldTime if row[rc.bal] == 0: newTrade = True return dframe
def getShares(self): ''' Utility to get the number of shares in this Trade. Each TradeObject object represents a single trade, or the part of a trade that happens on one day, with at least 1 transaction/ticket. ''' if len(self.df.iloc[0][frc.oc]) == 0: # If we are here, structjour has failed to determine long/short or balance # With no balance set, we can still set some kind of shares for market val for the day. # This value provides context info for the user. Set the best we can. # If shares are unbalanced, this figure will be ok self.shares = self.df[frc.shares].sum() # If shares are balanced (This should never run but ...) find max purchase/sell if self.shares == 0: maxbuy = self.df[frc.shares].max() maxsell = self.df[frc.shares].min() self.shares = maxbuy if max(maxbuy, abs(maxsell)) else maxsell return self.shares if self.settings.value('inputType') == 'DB': return self.getSharesDB() elif self.shares == 0 or not isNumeric(self.shares): if self.side.startswith("B") or self.side.startswith("HOLD+"): self.shares = self.df[frc.bal].max() else: self.shares = self.df[frc.bal].min() return self.shares
def __setMarketValue(self): ''' Set the market value based on shares * price. For incomplete trades, the info may be incomplete and a NaN value produced. A value is required. Set to 0 ''' mkt = self.getShares() * self.df.loc[self.ix0][frc.price] mkt = mkt if isNumeric(mkt) else 0 self.TheTrade.MktVal = mkt return self.TheTrade
def updateTSID(cls, tid, tsid): ModelBase.connect(new_session=True) session = ModelBase.session q = session.query(Trade).filter_by(id=tid).one_or_none() if q: if not isNumeric(q.trade_sum_id) or tsid != q.trade_sum_id: q.trade_sum_id = tsid session.add(q) session.commit() session.close()
def fixTsid(self, tdf): if len(tdf['tsid'].unique()) > 1: therightone = list() for id in tdf['tsid'].unique(): if isNumeric(id): therightone.append(id) if len(therightone) > 1: # This could be a place for user input...There are confused ids here # The question is what trade do these transactions belong to. raise ValueError( 'Programmers exception: Something needs to be done here') elif len(therightone) == 1: ibdb = StatementDB() for i, row in tdf.iterrows(): tdf.at[i, 'tsid'] = therightone[0] ibdb.updateTSID(row['id'], therightone[0]) # If one of the vals was nan, the column became float tdf = tdf.astype({'tsid': int}) # Update the db return tdf
def test_TheTradeObjectSetEntries(self): rc = FinReqCol() for tto in self.ttos: tto._TheTradeObject__setEntries() # if len(tto.df) < 4: # self.fail('This test requires a longer sample of transactions to run.') count = 0 x0 = tto.df.index[0] long = False r = tto.df.loc[x0] if len(r[rc.oc]) < 1 and (r[rc.side].startswith('B') or r[rc.side].lower().startswith('hold+')): long = True elif r[rc.oc] == 'O' and r[rc.shares] > 0 or r[ rc.oc] == 'C' and r[rc.shares] < 0: long = True # side = r[rc.side] for i, row in tto.df.iterrows(): count += 1 if (long and row[rc.shares] > 0) or (not long and row[rc.shares] < 0): if row[rc.price] != 0: entry = 'Entry' + str(count) ttoprice = tto.TheTrade[entry].unique()[0] self.assertEqual(ttoprice, row.Price, "Failed to set entry correctly") else: if row[rc.price] != 0: entry = 'Exit' + str(count) ttoprice = tto.TheTrade[entry].unique()[0] self.assertEqual(ttoprice, row[rc.price], "Failed to set exit correctly") if row[rc.PL] != 0: PLname = 'PL' + str(count) ttopl = tto.TheTrade[PLname].unique()[0] self.assertEqual( ttopl, row[rc.PL] if isNumeric(row[rc.PL]) else 0, "Failed to set pl correctly")
def postProcessingDB(self, ldf): ''' A few items that need fixing up. Locate and name flipped positions and overnight holds and change the name appropriately. Look for trades that lack a tsid (foreign key) and fix :params ldf: A ist of DataFrames, each DataFrame represents a trade defined by the initial purchase or short of a stock, and all transactions until the transaction which returns the share balance to 0. :return (ldf, nt): The updated versions of the list of DataFrames, and the updated single DataFrame. ''' rc = self._frc dframe = pd.DataFrame() for tdf in ldf: x0 = tdf.index[0] xl = tdf.index[-1] if tdf.at[x0, rc.bal] != tdf.at[x0, rc.shares] or tdf.at[xl, rc.bal] != 0: tdf.at[xl, rc.name] = tdf.at[xl, rc.name] + " OVERNIGHT" if tdf.at[x0, rc.bal] != 0: firstrow = True for i, row in tdf.iterrows(): # Cant fix anythintg if the balance is not set if row[rc.bal] is None: break if firstrow: side = True if row[rc.bal] > 0 else False firstrow = False elif row[rc.bal] != 0 and (row[rc.bal] >= 0) != side: tdf.at[xl, rc.name] = tdf.at[xl, rc.name] + " FLIPPED" break else: assert len(tdf) == 1 if len(tdf['tsid'].unique()) > 1 or not isNumeric( tdf['tsid'].unique()[0]): tdf = self.fixTsid(tdf) dframe = dframe.append(tdf) return ldf, dframe
def updateBal(cls, t, balance): '''Uses an ongoing session. Update balance only if there is no current balance''' if not isNumeric(t.balance): t.balance = balance ModelBase.session.add(t) return t