Useful Tools for Detecting Earning Manipulation and Fraud | by Henry Feng | Towards Data Science

In the summer term of MSBA, I was encountered with the biggest dataset I have ever dealt with at that point of time. It is the assignment from Financial Accounting course. The topic is related to detection of earning manipulation and fraud using companies accounting data.
The dataset is from Compustat, a database of financial, statistical and market information on active and inactive global companies throughout the world (quoted from Wiki). The instructor provided database to us. And our task is to go through different models and tools that are used to detect abnormality of earning.
The process is daunting but full of fun. After a month, I decided to revisit the assignments. In this article, I am going to introduce the models and tools I have learned for detecting earning manipulation and potential fraud.
This articles will be written in both theoretical and practical manners. I will try my best to elaborate what I know about the tools and also show the code how to achieve the results. The tools are applied to the yearly data from 1988–2017, and to every company in the data set, which showcase the overall picture of the earning manipulation trend.
In the next article, I will present how I used these tools to conduct earning manipulation research on Amgen, one of the world’s largest biotechnology company.
Main tools: Python Spyder and R
Skills used: Data cleaning with pandas, visualization with Matplotlib and ggplot2, linear regression, fixed effect regression
A. Cleaning and slicing the data
B. Simple exploratory data analysis
C. Indirect evidence of earning management
D. Benford’s Law
E. Discretionary Accruals Model
F. Operating Cash Flow and Accruals Model
Compustat is a huge dataset. In the data dictionary, it contains about nearly 1000 variables and the data is across nearly half of century with detailed company information. The CSV file of it is nearly 1.6GB, which a user couldn’t open it with a simple excel and loading it into R and Python takes a while.
In the assignment, my goal is to depict the economic truth of certain chunk of data. So I followed professor’s instruction of data cleaning and slicing, and I later on exported the data to csv file with appropriate size, which I will attached in my Github. If interested, feel free to download it and play around with it.
The data cleaning process takes several steps.
(1) Only pick several variables, including gvkey (company identifier), datadate (reporting period), fyear (fiscal year), revt, rect, ppegt, epspi, ni, at, oancf, sic, and rdq.
(2) Sample of year is limited ranging from 1988 to 2017.
(3) Drop any observation with missing assets, revenue, net income, EPS, account receivables, and operating cash flow.
(4) If PPE is missing, set it to zero.
(5) Drop any observation where revenue and account receivables are negative.
(6) Drop duplicate observation based on all columns except the column rdq.
Following the instruction, I wrote my python code as follows. After the process, the dataframe remained about 248,288 rows, which is acceptable.
compustat = pd.read_csv("compustat_1950_2018_annual_merged.csv")compustat_1 = compustat[["gvkey","datadate","fyear","revt","rect","ppegt","epspi","ni","at","oancf","sic","rdq"]]compustat_2 = compustat_1.loc[compustat_1["fyear"] > 1987]compustat_2 = compustat_2.loc[compustat_2['fyear'] < 2018]compustat_3 = compustat_2[compustat_2["revt"] >=0]com_3 = compustat_3[compustat_3['rect'] >= 0]com_4 = com_3.dropna(subset = ['at','revt','ni','epspi','rect','oancf'])com_5 = com_4.drop_duplicates(com_4.columns.difference(['rdq']))com_6 = com_5.fillna(0)
This part is trying to present an overall picture of the data I cleaned. I first simply calculate the average of revenue, net income and total asset of year 2017 for the combination of all the company in the file. The reason and logic instructor picked these three metrics for observation is quite straightforward. These metrics are easy to understand for the investor and they also become the first target for manipulation.
Further, I plot the trend of the three metrics from 1988 to 2017 with the simple chunk of code.
df2 = df.groupby('fyear').agg({'revt':'mean', 'ni':'mean', 'at':'mean'})plt.figure(figsize=(10,8))plt.plot(df2['revt'], linestyle = 'solid')plt.plot(df2['ni'], linestyle = 'dashed')plt.plot(df2['at'], linestyle = 'dashdot')plt.legend(df2.columns.values.tolist())plt.title('Trend for Revenue, Net Income & Total Asset 1988-2017')plt.show()
From the visualization above, we can tell that average of total asset increase drastically compared to revenue and net income. For the whole industry, the fiscal number of asset is growing. Does this indicate to some kind of manipulation? (We don’t know for now). And for net income, its average hasn’t change a lot across the years. Maybe it implies that the quality or calculation of net income make it harder to manipulate. I will go further and reveal more detail later on with different utilization of the tools.
Next, I calculated the change of earning per share (EPS) and return on asset (ROA) from year t to t-1 for each firm year, and further plot it as histogram within the range of -0.1 and +0.1. The graph can grant us a more indirect hint of possible earning management.
I used group by and shift function for creation of the lag year EPS and ROA.
df3 = dfdf3['previous_eps'] = df3.sort_values(['fyear']).groupby('gvkey')['epspi'].shift()df3['eps_change'] = df3.epspi - df3.previous_eps
And with Matplotlib, I created the two similar graphs.
plt.style.use('seaborn-white')plt.figure(figsize = [15,6])plt.hist(df4_eps, bins=20, align = 'left')plt.plot((0,0),(0,8000),'r--')plt.title('EPS Change Distirbution')
The graphs show that the distribution of EPS is more asymmetric than the ROA across all the companies in the dataset. And also it is observed that, for EPS, the frequency of positive numbers which are little greater than zero, meaning that every year, companies tend to perform better on EPS than previous year.
There are several reason for the difference. Earning management might be one, and other reason like the manager’s tenancy of working harder is possible too.
Usually, EPS is the metrics that stakeholders and market care about more. And it reflects the performance of a manger more obviously than ROA. Furthermore, EPS can be manipulated or controlled by buying back or releasing more stocks into market. These may be the reasons of graph symmetry difference of the two key metrics.
Turning into a single company scope, it can also give us a glimpse of evidence of earning manipulation. I can subset the data with certain company, plot the symmetry graph just like the above, and observe the abnormality.
Benford’s Law, also called first-digit law, is an observation about the frequency distribution of leading digit in many real-life sets of numeric data (quoted from Wikipedia). With a big set of data or column value, Benford’s Law tell us the first digit appearance is followed by some rules and probability. Therefore, if the result of count/ frequency has some deviation from the Benford’s Law, there is a higher possibilities of manipulation.
Followed by the instruction from teacher, I switched tools from Python to R. The benford.analysis package can easily calculate the first and second digit of columns. I used revenue, operating cash flow and total asset as observations.
revenue_bf <- data.frame(benford(abs(df$revt), number.of.digits = 1, sign = 'positive', discrete = TRUE, round = 3)$bfd)[,c("digits","data.dist", "benford.dist")]colnames(revenue_bf) <- c('Digit', 'Sample_Revenue', 'Benford_Distribution')ggplot(data = revenue_bf, aes(x = as.factor(Digit), y =Benford_Distribution)) +  geom_bar(stat='identity') +   geom_line(aes(Digit, Sample_Revenue, col = 'Sample_Revenue'), linetype = 1) +   geom_point(aes(Digit, Sample_Revenue), size = 4, col = 'red')+  ggtitle('Theoretical Distribution v.s. Sample Revenue Distribution - 1st Digits ')
From the three graphs above, we can see the theoretical and sample distribution of first digit from three columns. Visually, there is no such huge difference.
I decided to exploit chi-square test to see if there is actually no significance difference between theoretical and sample distribution. The result is below.
From p-value, it is not that surprising that operating cash flow is not significance. However, it is out my expectation that revenue doesn’t deviate much based on the test, which indicates low possibility of manipulation at first digit. And for asset, it has higher probability of manipulation of first digit where its p-value is low and significant. Remember the graph of asset over year? Its average is growing a lot. From Benford’s Law, I further confirm that the higher possibility of manipulation in asset over all companies.
After looking into first digit, second digit of each specific columns is worth investigating too. Slightly different from first digit, second digit contains digit zero, which alter the distribution percentage of each digit. The results of three metrics are shown below.
From three graphs above, it is very obvious that the frequency of zero in the sample revenue, cash flow and asset is higher than theoretical Benford’s distribution. A possible explanation is that when companies calculate the numbers, they might tend to round 9 to 10, and it leads to more 0 and less 9. It might be intentional or unintentional, but it definitely worth more check, especially when analyzing single company.
From the chi-squared test, the p-value of all three metrics are quite significant. And among all revenue is the most significant one, which may refer that manager tend to do some trick such as rounding for revenue to have better numeric performance.
Instructor told us SEC use discretionary accruals model to screen firms for improper earning management. The formula is below:
In this OLS model, accruals is net income minus operating cash flow. And every variable except SIC is from previous year number. Therefore, to put it simply, the OLS model is trying to test the predictive power of previous performance metrics on the this-year accruals. And SIC is the industry classifier, which means that for different industries, there might be different groups and levels of estimates that might grant different accruals.
Therefore it is a good idea to use panel data regression to run the whole dataset. The code is below.
fixed_effect.accrual <- plm(accrual ~ cash_revenue + ppe + sic, data= accrual_df, index = c('sic'), model = 'within')
After fitting the line, the true goal for the model is to find the absolute value of residuals. The interpretation of this is that if a company, compared to other companies in the same industry, have higher absolute value of residuals. Its earning is more deviated from the industry standard, which has higher possibility of manipulation.
accrual_df$residual <- fixed_effect.accrual$residualsaccrual_df$residual <- abs(accrual_df$residual)
I will showcase the process for company Amgen in the next article.
The model is also used OLS predictive power to predict the next year operating cash flow using accruals and previous year operating cash flow. The formula is below:
I decided to fit the regression with every rows in the data set. The code is like this. And we get the summary of the fitted regression.
df$accurals = df$ni - df$oancfdf2 <- df[,c('gvkey','fyear','oancf')]df3 <- mutate(df2, fyear = fyear - 1) %>%  rename(., next_oancf = oancf)df <- left_join(df, df3, by = c('gvkey',"fyear"))oc_reg <- lm(df$next_oancf ~ df$accurals + df$oancf)summary(oc_reg)
From the summary, the p-values of accruals and prior cash flow are significance, which means both are highly correlated to the next-year operation cash flow, and are positive related to the target variable.
After that, the instructor asked us to divide the data into two set, before and after 2002. Thus, I ran the same model on the two set of data and get the summary of two fitted OLS models.
From the summary above, we can tell from r-squared and estimate that the predictive power of the model is more significant before 2002. The economic truth of the difference might be with more manipulation of accruals or operating cash flow after 2002, the model’s predictive power was distorted since then.
With this model, it is also useful for detecting single company manipulation of cash flow. The tactic is subset the data into equal time range and observe the predictive ability of each period to detect possible earning management. I will conduct this again in my next article as well.
In this articles, I tried my best to articulate the tools I learned in class and showing the process of applying these tools for the overall data. It is a really challenging process, but by revisiting the dataset as well as these models, I start realizing how deep it can be for further research. And I will for sure continue my journey toward fraud and earning manipulation with data in the near future.
Special thanks to my instructor of Financial Accounting class, who granted me with a inspiring summer.
All the code can be found in my Github. Feel free to give me some feedback.
If you like the article, feel free to give me 5+ clapsIf you want to read more articles like this, give me 10+ clapsIf you want to read articles with different topics, give me 15+ claps and leave the comment hereThank for the reading