This action might not be possible to undo. Are you sure you want to continue?
MASTER OF BANKING AND FINANCE
20102011
COHERENT STRESS TESTING
A Bayesian Approach to Scenario Analysis and Stress Testing
GRADUATION PROJECT
ADVISORS Rudi Vander Vennet Michael Frommel Nikolas Vander Vennet Wim Konings
AUTHORS KEVIN HOEFMAN MAXENS BERRE
1P a ge
Introduction
For the past several decades, financial crises have highlighted the need to assess the stability of the financial system. The current global financial crisis has demonstrated limitations in the purely statistical approaches which have heretofore been employed to assess the stability of financial institutions. During 2007 and 2008, several multiplestandarddeviation loss events, predicted to only occur once every several thousand years occurred in the space of 24 months. Clearly, the models in use were no longer functioning.1 In January 2009, during the depths of the financial crisis, the Basel Committee on Banking Supervision published its view that the bank stresstesting which had heretofore taken place was insufficient for a number of reasons. The views of the BIS were that the long period of historical stability preceding the crisis reduced likelihood that stress test was detecting severe shocks and systemic weaknesses. Furthermore, estimations of “severe” were dramatically underrated and neither correlation of shocks nor rational feedback effects were being taken into account. The effects of recent financial innovations were also being ignored.2 Essentially, the financial market had grown considerably more complex and less transparent since the previous round of financial crises at the end of the 20th century. The BIS’ concern is that stresstesting had not kept up with the times. In 2009, the Committee of European Banking Supervisors (CEBS), an independent advisory group on EU banking supervision implemented the first EUwide stress tests. These stress tests were also executed in accordance with frequentist methodology. 22 major EU crossborder banks were examined. Potential 20092010 trading and credit losses could amount to almost € 400 Billion in the adverse scenario. Nevertheless, none of the banks in the test saw their Tier1 ratios potentially fall below 6%. The European Council subsequently requested that further stress testing be done with broader objectives and measuring the ability to absorb further shocks. Subsequently, in July 2010 a more inclusive battery of stress test examining 91 banks was undertaken by CEBS, in which, five banks failed. This secondround of stress tests was undertaken amid concerns that the September 2009 round was insufficiently strict in its examinations and that, while large systemic banks on the European scale were examined, not enough of the banks systemic to individual membernations were accounted for in the CEBS 2010 stress tests. Furthermore, because illiquidity was assumed to be the primary threat, assets in the bank’s trading book were targeted while heldtomaturity assets held in the banking book were not. The latter represented approximately 83% of bank assets. Another shortfall of the 2010 stress tests was the assumption that there would be no sovereign defaults in the EU. A better approach would have been to also attempt to determine which scenario could have led to a sovereign default in the EU.
1 2
Rebonato, 2010 BIS, 2009
2P a ge
The subsequent 2011 European banking stress test is currently being performed by the newly founded European Banking Authority. The 2011 exercise incorporates lessons learned as well as policy demands from Economic and Financial Affairs Council (ECOFIN) and the Economic and Financial Committee (EFC). One can take the fact that the EU has gone through three rounds of stress testing as an indication of dissatisfaction with the thoroughness of the stress tests in unearthing bank vulnerabilities throughout the EU. A different approach was needed. The European stress tests tried to answer the question of what would happen in the event of both current loss expectations as well as a worstcase scenario whereby losses exceeded current loss expectations. However, the stress tests consider each potential source of loss as a separate aspect, without accounting for dynamics of the situation where if one shock happens, it can have a causal effect on the other shocks. Thus, they have left something to be desired, irrespective of the number of banks or percentage of GDP investigated. According to Rebonato, the key missing ingredient from stress testing procedures is a solid way of capturing the corelationships between the various stress events. Rebonato suggests that subjective expert assessments for the marginal probabilities of the various stress events and the conditional probabilities between them, and a Bayesian procedure for consistently tying these together, can lead to a better understanding of the dynamics of the stress scenario that is being considered. This allows us to generate statistical information about stress losses that are more realistic than losses captured by conventional stress testing methodologies. The rest of this paper proceeds as follows. The following section discusses what progress has thus far been made in stress testing, as well as the way forward. Section three presents a hypothetical Bayesian stress scenario. A bank and selected and its situation described in detail. Then sources of vulnerabilities are analyzed and a stress scenario is presented along with its accompanying topology. Lastly, the final section provides concluding analysis.
Where Are We With Stress Testing? Where do We Need to Go?
While economists have made great bounds in terms of designing scenarios, more progress is needed. In the view of the Basel Committee on Banking Supervision, there are major shortcomings which need to be addressed, related to concerns that the financial world had fallen into a sense of security due to the length of time since the last crisis and that systemic risks, correlation of shocks, and the effect financial
3P a ge
innovations were being overlooked in stress testing. 3 The current shortcomings in stresstesting can also be seen in the European Council’s reception of the 2009 and 2010 CEBS stress tests. The traditional approach to stresstesting applies loss estimates that are ultimately based on frequentist techniques. In order to come up with an adverse scenario for a stress test, one first needs to have an understanding of what the “normal” scenario is. This idea of what is “normal” is derived from frequentist techniques like VaR. This approach has the drawback of being backwardlooking. Another drawback comes in the area of extremely rare events. In order for the frequentist approach to generate meaningful results, one needs a relatively large amount of relevant data. Extremely rare events are difficult to analyze with a frequentist approach, meaning that assigning stress losses to them is equally difficult.4 What is needed is a forwardlooking and conditional approach to losses, which can be applied in situations with little relevant data and in dynamic situations. In the view of both Keynes and Taleb, the frequentist approach has the major drawback that it relies that the future is like the future in that it is like the past, predictable, calculable to within a certain bound, and therefore hedgeable. This reaches to the basic difference, in the Keynesian view, of risk and uncertainty. Under uncertainty, “No scientiﬁc basis on which to form any calculable probability whatever. We simply don’t know.” 5 By contrast, the prevailing neoclassical economic view is that market participants have complete knowledge of probability distributions over future events. In other words, we face only measureable risk.6 A further major drawback of the frequentist approach, expressed by Rebonato, is the unavailability of sufficient quantities of relevant data concerning extremely rare situations. Fundamentally, in order for a frequentist approach to be valid, data must be drawn from identical conditions. If however we are gathering data on a black swan event or black swan scenario, we may find ourselves needing to draw data from a wide breadth of sources and/or across a long timehorizon. Unfortunately, if we engage in either drawing data from the distant past or from dissimilar situations, we cannot accurately use it for frequentist analysis.
WHAT IS NEEDED IN STRESS TESTING?
Given the shortcomings enumerated, some improvements are necessary. The most important aspect needed in stress testing in order to overcome black swan events is a forwardlooking element. Subjective probability is a plausible way in which this can be achieved. A subjective probability is essentially an opinion – hopefully a wellinformed expert opinion – regarding the probability distribution of an event occurring. While there is no mathematical proof behind the
3 4
BIS, 2009 Rebonato, 2010 5 Keynes, 1937 6 Skidelsky, 2009
4P a ge
answer, one can expect that in addition to historical probability distribution, a subjective probability might be influenced by expectations, indicators as to what may occur in the future, and indicators as to why this time might be different. The most significant upsides to this approach are the incorporation of a wide range of indicative and qualitative data, as well as the nonreliance on vast and often difficulttoobtain amounts of data. The latter factor would render this approach particularly valuable during scenarios involving one or more extremely rare (statistical tail) events.7 Unfortunately, subjective probability is still subjective and might in fact be vulnerable to a high degree of bias. Nevertheless, bias can be overcome by via the use of the law of larger numbers. That is, several expert subjective judgments can be employed in order to construct the subjective probability. This would particularly be the case in situations in which a diversity of opinions and views could be assembled. Within a financial institution this can be achieved by polling experts and risk managers of different departments, regional offices and business segments on the same probabilistic views. Another important issue which needs to be addressed in stress testing is correlation of shocks. In many stress tests, this has truly come to be a key missing ingredient. The simple fact that a financial institution survives an economic shock may might not be perfectly indicative of banksurvivability when the economic shock in question might also set off (or might otherwise be associated with) a chain reaction of economic shocks. In order to approach this within the context of stresstesting, conditional probabilities can be utilized. In contrast, stress testing methodology so far relies chiefly on marginal probabilities. The question answered by this method is “Assuming that a specific economic shock (or set of shocks) takes place, what is the likelihood of bank failure?” A Bayesian approach to stress testing would instead ask the question “Assuming that a specific economic shock (or set of shocks) takes place, what is the likelihood that this will trigger other shocks as well? What would then be their effect on a financial institution at each given stage? And finally, is bank failure likely to occur at any of these stages?”
THE BAYESIAN NET
A Bayesian net is a way of modeling the relationships between the various stress events that are being considered in a single stress scenario. For instance, given a default of a Euro zone country, the risk free rate (German government bond) is likely to spike upward and equity markets will crash given that investors are likely to have a riskaverse reaction. One possible Bayesian net for this scenario looks like this:
7
Rebonato, 2010
5P a ge
E1: Euro zone Country Default
E2: Yield curve shift
E3: Equity market crash
The risk manager creates a Bayesian net according to his idea of how reality works. He assigns marginal probabilities to the events in the net, and conditional probabilities to the causal relationships. Different risk managers could come up with different Bayesian nets and/or probabilities for the same stress test. Since all Bayesian nets are simplifications, there isn’t one “true” Bayesian net. The Bayesian net ultimately expresses a statement of conditional independence reflecting the causal assumption that we intend to propose. While correlations and conditional probabilities don’t necessarily give information about causality, we can use expert subjective judgment to determine a causal structure. Consider the diagram above. Setting aside the fact that any of these three events can take place with probability P(Ei) and that each of these three events has its own direct and independent effect on a financial institution, the Bayesian net also indicates that, once we know that event E1 has occurred, there is a higher probability that the events E2 and E3 will occur. Ultimately, we can use the Bayesian net in order to determine the probability of joint occurrence of any combination of the three economic shocks. As we can see from the Bayesian net, these probabilities are not independent. Given such a structure, the probabilistic relationship between the occurrence of shock E1 and the joint occurrence of E1 and E2 is given by the relationship:
PE2  E1
P( E1 E2 ) P( E1 )
In this scenario, there are six conditional probabilities.
PE2  E1 PE3  E1 PE1  E2
PE2  E3 PE3  E2 PE1  E3
6P a ge
One additional useful fact about the Bayesian net is that large and complex Bayesian nets are able to provide us a powerful deductive tool in cases when we only have some of requisite information.
7P a ge
DECONSTRUCTING A BAYESIAN NET
We can use the relationship between conditional and joint probabilities to deconstruct the joint occurrence of any combination of events into a functional form. Consider the following Bayesian net:
The probability of these events happening at the same time is:
P(A ∩ B ∩ C ∩ D)
Since for any two events E1 and E2:
PE2  E1
It follows that:
P( E1 E2 ) P( E1 )
<=>
P(E1 ∩ E2) = P(E2  E1) . P(E1)
P(A ∩ B ∩ C ∩ D) = P(A  B ∩ C ∩ D) . P(B ∩ C ∩ D)
The joint probability on the right hand side of the equation can further be deconstructed by using the same rule. If we follow this through to its logical conclusion, we get:
P(A ∩ B ∩ C ∩ D) = P(A  B ∩ C ∩ D) . P(B  C ∩ D) . P(C  D) . P(D)
This principle of breaking down the full joint probability into a combination of conditional and marginal probabilities is the first step towards solving the Bayesian net. A more practical way of deconstructing the Bayesian net is by ordering the events from bottom to top before deconstructing the model into a functional form. If we start from a reverse ordering, we can show that:
8P a ge
P(D ∩ C ∩ B ∩ A) = P(D  C ∩ B ∩ A) . P(C  B ∩ A) . P(B  A) . P(A)
Of course, the end result should be the same. The rationale for this reverse ordering will become clear in the next chapter.
CONDITIONAL INDEPENDENCE
The Bayesian net orders the events in a parentchild relationship. In our diagram:
Node A is a parent node of node D and node C. Node D and node C are children of node A. Node B is also a parent of node C, but not of node D. We can see that node A is the only node that affects node D. As a consequence, the probability of D following from A, B and C is equal to the probability of D following from just A. This can written as:
P(D  C ∩ B ∩ A) = P(D  A)
Node C is affected by node A and node B, but not by node D. By the same reasoning, the probability of C following from A, B and D is the same as the probability of C following from A and B:
P(C  D ∩ B ∩ A) = P(C  B ∩ A)
In general, a probability where a node is dependent on its parents and other nodes that are not its parents can be reduced to the probability where that node depends only its parents. It is said that nodes C and D are conditionally independent: the nodes aren’t independent from one another (since they are still connected through their parent A), but once the states of their parent A is known, node C contains no extra information that would help us determine whether event D has occurred, or vice versa.
9P a ge
We can put the rules of conditional independence to use, and further reduce the functional form of the Bayesian structure from the point where we left off in the earlier chapter:
P(D ∩ C ∩ B ∩ A) = P(D  C ∩ B ∩ A) . P(C  B ∩ A) . P(B  A) . P(A)
with Which leads to:
P(D  C ∩ B ∩ A) = P(D  A)
P(D ∩ C ∩ B ∩ A) = P(D  A) . P(C  B ∩ A) . P(B  A) . P(A)
INDEPENDENCE
When two nodes are fully independent, it means that they don’t contain information about each other’s occurrence. In the diagram we are considering:
Nodes A and B are considered to be fully independent of one another. As a result, the conditional probability of one happening if the other has happened is equal to the probability of one happening regardless of whether the other has happened:
P(A  B) = P(A) P(B  A) = P(B)
Note that it is not correct to state that P(DC) = P(D). Node D and node C may be conditionally independent, but they are not independent since they are connected through their parent A (independence and conditional independence are different concepts with different rules).
10  P a g e
Applying this principle, we can reduce the functional form of this model to its final form:
P(D ∩ C ∩ B ∩ A) = P(D  A) . P(C  B ∩ A) . P(B  A) . P(A)
with Which leads to:
P(B  A) = P(B)
P(D ∩ C ∩ B ∩ A) = P(D  A) . P(C  B ∩ A) . P(B) . P(A)
The final functional form consists of marginal probabilities of the parent nodes and conditional probabilities of the occurrence of the parent nodes causing the occurrence of their children. This functional form will be used by the program (see section below) to create the table of joint probabilities for all combinations of events.
The Program
TECHNOLOGY
The program is created in Excel and written in Visual Basic for Applications (VBA).
DRAWING A BAYESIAN NET IN EXCEL
First of all, the risk manager needs to decide what the events are that he or she wishes to consider. These events are drawn in the structure of a Bayesian net. If we take the diagram:
11  P a g e
In this scenario, a risk manager considers four possible events A, B, C and D. Event A could be an equity crash, event B a housing market crash, and so on. The Bayesian net is entered into Excel in a twodimensional matrix form. A matrix representation of the diagram above looks as follows: A 1 0 1 1 B 0 1 1 0 C 0 0 1 0 D 0 0 0 1
A B C D
The diagonal in the matrix is filled with ones. The upper right triangle is filled with zeros. These values aren’t used by the program. The connection structure of the Bayesian net can be read from the lower left triangle, where the ones indicate which nodes are connected to which: the titles of the columns indicate where the connections start, whereas the titles of the rows indicate where each node is connected to. The relevant ones are marked in bold. A VBA program is able to read values from cells in Excel. This allows the program to understand what the Bayesian net looks like.
ENTERING THE PROBABILITIES
Each of the events has a probability of occurring within the time frame that the risk manager chooses to consider. This marginal probability is the probability that the event occurs during the chosen time frame (regardless of how it is triggered). In addition, the Bayesian net shows that if event A occurs, there is a probability that A will trigger events D and C. Similarly, event B can trigger event C. The risk manager needs to make an assessment for the following pieces of information: P(A): the marginal probability that event A occurs P(B): the marginal probability that event B occurs P(C): the marginal probability that event C occurs P(D): the marginal probability that event D occurs P(DA): the conditional probability that event A, when it occurs, triggers event D P(CA): the conditional probability that event A, when it occurs, triggers event C P(CB): the conditional probability that event B, when it occurs, triggers event C P(CA∩B): the conditional probability that events A and B, when they occur together, trigger event C 12  P a g e
In general, the risk manager needs to specify a marginal probability for each of the events, and a conditional probability for each parentchild connection inside the Bayesian net. Note that while related, the conditional probability of C being triggered by events A and B occurring together does not follow directly from the conditional probability of A triggering C and the conditional probability of B triggering C. For example, the occurrence of A and B happening at the same time could lead to a dramatically higher probability of C occurring than if A or B would occur separately. The risk manager takes this into consideration by supplying both the singleconditional probabilities and the doubleconditional probability. Since these are probabilities, the risk manager will supply values between 0 and 1 for each.
DOUBLECONDITIONED PROBABILITIES
A doubleconditioned probability arises from the situation where two parent nodes share one child node. In the example we are considering there is one doubleconditioned probability: where A and B are the parent nodes and C is the child node. Rebonato assumes that there won’t be any networks where nodes can have more than two parents. This means that doubleconditioned probabilities are the most complex conditioned probabilities we can encounter. Unfortunately, the book is incomplete as far as doubleconditioned probabilities go (see A Word of Caution About The Book, below). In order to allow the program to solve doubleconditioned probabilities, the risk manager needs to supply one more conditional probability: the one where one parent leads to the other parent. In our example, this would be P(AB). When the parent nodes are independent this is easy: P(AB) should then be given the same value as P(A), according to the Bayesian rules of independence. If the nodes are not independent but instead conditionally independent, things get more complex. See A Word Of Caution About The Book, below for a derivation.
CHECK IF ALL VALUES ARE ENTERED
Based on the matrix representation above, the program knows the names of the nodes and the connections between them. The program uses this information to deduce what the required probabilities are, and checks the rest of the Excel sheet for their values. The program will prompt the risk manager if any values are missing.
13  P a g e
SANITY CHECKS
It is possible that the risk manager provides values that by themselves look correct, but when combined imply an impossible situation. For example, consider the following triplet of values: P(A) = 0,16 P(B) = 0,03 P(BA) = 0,25
Each of these probabilities look fine by themselves (they are numbers between 0 and 1). However, combined they imply the following impossible situation: Out of 100 situations, there will statistically be 16 situations where A occurs. 25% of these situations lead to B also being true. Meaning that out of 100 situations, there are 4 instances where B is true following from the fact that A is true. However, the probability of B in itself is only 3%. There should only be 3 cases out of 100 in total where B is true! The number of instances of B occurring as a result of the occurrence of A is too high.
These kinds of impossible situations can be checked for by applying Bayes’ theorem. In our example: P(AB) = P(BA) . P(A) / P(B) P(AB) = 1,3333 (!) with P(BA) = 0,25; P(A) = 0,16; P(B) = 0,03
<=>
The resulting conditional probability is higher than 1. This is an impossible value. This procedure allows the program to recognize that the values that the risk manager supplied are inconsistent. The program checks all possible conditional probabilities that can be created from the values that the risk manager supplied, and verifies that in no situation there is a probability that is not between 0 and 1. This is the process of sanity checking. The sanity checking process will warn the risk manager which values are inconsistent, if any. The risk manager can then enter new values. In our example, the risk manager could lower the probability that A happens, lower the conditional probability that B happens if A happens, or raise the probability that B happens. Or a combination of all three.
THE JOINT PROBABILITY TABLE
Once all required values have been found and are consistent, the program deduces the functional form of the Bayesian net from the matrix representation, by deconstructing the joint probability and applying the rules of conditional independence and (full) independence. The program then calculates the full joint probability table by applying the rules of Bayesian logic on the values that the risk manager 14  P a g e
entered. While this is a calculationheavy process, the risk manager only needs to sit back and wait for the results.
GAINS AND LOSSES
The program allows the risk manager to associate losses (or if appropriate, gains) for each of the events. These losses are used to calculate extra information: since a probability is calculated for every combination of events, it is possible to calculate the loss that is associated with that combination of events, and the probabilistic loss. This can be used to calculate various forms of capital (economic, regulatory, etc.
THE PROGRAM CODE
A full listing of the program code can be found in Appendix 7.
A WORD OF CAUTION ABOUT THE BOOK
While Rebonato‘s insights into stress testing and his views on how to take it to the next level are commendable, the way he puts the material in practice in this book leaves a lot to be desired. We were surprised to discover that the procedure which Rebonato describes as a general way to generate the joint probability table doesn’t actually work when one tries to put it in practice. This is because of two reasons: In Chapter 11, the transition in 11.24 is mathematically incorrect as it does not follow from the Bayesian rules as Rebonato claims. However, the further development of the procedure is based on this transition. Also in Chapter 11, Rebonato gives a partial explanation for how to break up doublyjointed probabilities. Formula 11.32 contains a term P(CD) that can’t be solved in a trivial way. However, Rebonato concludes the explanation at this point with the sentence “The good news is that, given the limited topological complexity (…), matters do not get any more complex”. Unfortunately, matters really do get more complex.

We created a new procedure for handling doublyconditioned probabilities, and used this instead of the one that Rebonato suggests in Chapter 11. The current procedure for evaluating a Bayesian net is based on the principles that are explained in Chapter 8, not on the procedure that can be found in Chapter 11. In order to make it work, we had to find a mathematically consistent way to obtain the term P(CD) from 11.32, and for resolving doublyconditioned probabilities in general. 15  P a g e
We emailed Dr. Rebonato about the term P(CD). He was quick to help us, confirming the situation by replying that his thoughts on the matter have evolved a lot since he wrote the book and sending us a draft of a new book that looks at the same problems in a somewhat different light. We decided to stay with the old book for the scope of this thesis since that was its topic. However, we used the confirmation of Dr. Rebonato as a basis for coming up with a way of solving the term in question that isn’t in the book (credit Dr. Nicolas Vander Vennet and Wim Konings of Deloitte). This derivation is:
P(D ∩ C)
= = =
P(D ∩ C ∩ A) + P(D ∩ C ∩ Â) P(DC ∩ A) . P(CA) . P(A) + P(DC ∩ Â) . P(CÂ) . P(Â) P(DA) . P(CA) . P(A) + P(DÂ) . P(CÂ) . P(Â)
with P(D ∩ C) = P(DC) / P(C)
This way of solving the term P(CD) and similar terms in general allows us to reduce Bayesian nets with doublyjointed probabilities.
LINEAR PROGRAMMING
Rebonato explains two ways of coherent stress testing. The methodology explained in Chapter 10 relies on linear programming. The methodology explained in Chapter 10 relies on Bayesian nets. Suppose that the risk manager would supply values for the conditional probabilities P(AC) and P(CA). These values are not independent of one another: they are linked through the marginal probabilities of P(A) and P(C) by Bayes’ theorem. A risk manager might accidentally enter values for the conditional probabilities that are inconsistent with one another. Linear programming is a technique that checks whether conditional probabilities are consistent and if not, it alters the values in such a way that they are made to be consistent. Since our program uses the Bayesian net approach, a risk manager needs only to provide conditional probabilities in one direction: from top to bottom. While it is certainly true that there are checks to be done on the values that the risk manager supplies (the sanity checks), the checks that are performed by linear programming aren’t relevant to us: the conditional probabilities will never be inconsistent in the way that linear programming checks for. As such, the linear programming technique, while interesting in itself, had no added value to our program. Also: in an email exchange we had with Dr. Rebonato, he stated that he no longer uses linear programming.
16  P a g e
The Scenario
TARGET BANK
The target bank for the stress scenario is Österreichische VolksbankenAG. The bank’s activities are diversified into five strategic segments. These are retail banking, real estate, corporate banking (corporate segment), and investment banking (financial markets segment) and asset management (investment book/other operations segment). Overall, Volksbank has 594 subsidiaries, most of which as whollyowned. For the most part, the Volksbank subsidiaries are headquartered in Central and Eastern Europe. Volksbank’s retail banking operation is active in Austria via VB Leasing Finanzierungsgesellschaft m.b.H. and in Bosnia, Croatia, Czech Republic, Germany, Hungary, Malta, Romania, Serbia, Slovakia, Slovenia, Russia, and Ukraine via Volksbank International (VBI) and via VBLeasing International Holding GmbH. Furthermore, Volksbank has a large number of daughter companies spread throughout Central and Eastern Europe and operations in the CEE region have been ongoing since 1991.
STRATEGIC BUSIENSS SEGMENTS
Volksbank AG
Corporate Segment
Retail Segment
Real Estate Segment
Financial Markets Segment
Investment Book/Other Operations Segment
The Retail segment of Volksbank’s business provides various banking and related financial products and services, including deposit products, lease financing, project and real estate financing, treasury products, and advisory services to private individuals, and small and medium sized enterprises. Furthermore, the retail segment involves retail banking in both Austria and in the Central / Eastern European region. Retail operations are divided into two companies. VB Leasing Finanzierungsgesellschaft m.b.H. is 17  P a g e
responsible for domestic operations in Austria, while Volksbank International owns ten daughter companies responsible for retail banking in Central and Eastern Europe. 2010 domestic operations were the most successful in Volksbank’s history8. Volksbank’s International holdings meanwhile, experienced a small decline in total value of assets. As outlined in the segmental reporting for 2010 displayed in the appendix, this is the largest of Volksbank’s business segments in terms of net income and the secondlargest in terms of total assets, comprising 72% and 35% respectively of overall Volksbank figures. In addition, this operational segment is characterized by a large concentration of Volksbank’s exposure to Central and Eastern European markets. This is an area of high initial vulnerability. Because success in the retail banking segments depends on generation of new loans, successful rates of loan repayment, and deposits, this may be an initial point of shockentry, where negative and positive economic shocks might first enter the banking system and start to affect Volksbank. Furthermore, a shock in this commercial segment might lead to further risks within the bank. It would therefore be reasonable to assign shocks to this segment of Volksbank a relatively high spot on the Bayesian net topology. The bank’s real estate segment focuses on commercial real estate financing as well as real estate leasing and real estate project development (each via different subsidiaries) in both Austria and the CEE region. Furthermore, real estate project development is done in collaboration with partners and coinvestors from both the international public sector and private sector entities other European countries. Performance in this segment improved during 2010 and risks were reduced significantly visàvis 2009. This is another potential entry point through which economic shocks might enter the banking system. During the 2008 financial crisis, the collapses of asset values within the real estate sector in the US were the first shock which touchedoff the worldwide financial crisis. For Volksbank, this is another business segment in which expose to the CEE market is concentrated. Moreover, Volksbank’s exposure to the region in this segment reaches beyond the EU’s borders as far as Russia, making this segment potentially more volatile than other Volksbank business segments. Thus, a high place in the Bayesian topology should be assigned to shocks emanating from this segment. Volksbank’s corporate segment provides banking services to firms and entrepreneurs. This includes a diverse range of services ranging from project finance to private equity, M&A, leveraged finance, corporate lending, and syndicated financing. Germany is the core market for the corporate segment. The 2010 Annual report indicates that corporate lending was muted because corporate spreads were high and that client firms were in fact not expanding their operations nor their physical capital. Export financing meanwhile saw an increase in demand for transactionlinked loans. As for project finance on the other hand, 2010 was a successful year. While it is likely that corporate banking may suffer some vulnerability, due to generalized downturns in corporate activity and profitability, any initial shocks suffered in this area are likely to be highly localized. In the event of a wider stress situation however, a generalized exposure of corporate counterparty
8
Volksbank, 2010
18  P a g e
clients to an economic shock would threaten the sustainability of this business segment within Volksbank. Therefore, in a reallife situation, it would be likely that a generalized shock to this segment would emerge only as a secondary step within a crisis scenario. The financial markets segment offers exchange rate, derivatives, money market, and interest and price products, as well as involves in securities and foreign exchange trading, and asset management businesses. The investment book/other operations segment in concentrated on management of Volksbank Group’s assets. It is the group’s largest segment in terms of total assets, but has seen negative net income in both 2009 and 2010.9 While Volksbank group’s geographic focus is primarily on Central and Eastern Europe, the segment reporting by regional markets reveal that 81% of income from financial investments is drawn from “other markets” in 2010, while 90% of income from financial investments was drawn from Austria in 2009.10 The potential vulnerabilities of these two segments lie in their international transmission roles. Essentially, this segment could turn into a transmission mechanism for contagion in the CEE region, as is true for this segment’s counterparts in competitor banks in the CEE such as KBC and Credit Agricole. It is therefore plausible that regulatory authorities in the CEE region may act in order to either respond to or preempt contagion transmission. Such action might take the form of capital controls affecting foreign exchange positions and transactions or special taxes and restrictions suddenly erected visàvis positions in specific asset classes. Another viable possibility would be that of sudden radical loss in asset values and portfolios. These could come as a result of bond defaults (either sovereign or corporate) or the crash of equities markets. This in turn could affect the bank’s overall financial health and even bring problems home to Austria, as assets would have to be reduced in order to maintain liquidity and solvency. Such an event should be assigned an intermediate position within a Bayesian topographical structure.
CENTRAL AND EASTERN EUROPE
The Central and Eastern European market is quite an interesting market for in which to analyze bank operations due to its position on the frontier of the EU. This market is constituted by a dynamic mix of Eurozone countries, peripheral nonEurozone EU countries and non EU countries. Operations in this area play a central role in Volksbank’s activities and Volksbank’s exposures. According to the 2010 Annual Report, 71% of Volksbank’s net income flows from CEE region operations while 18.6% was drawn from Austrian operations. Despite the fact that
9
10
Volksbank, 2010 Ibid
19  P a g e
Nevertheless, the NonEU regions of Central and Eastern Europe suffer for much higher volatility than do the core EU countries. Ukraine suffered a 15.1% GDP decline in 2009 and returned to positive GDP growth figures the following year while Romania swung from a 7.3% GDP increase in 2008 to a 7.3% contraction the following year.11 It would appear that Volksbank takes seriously the risks of doing business in the CEE region. According to its 2010 Annual Report, risk provisions for operations in Austria were 36% of regional net income. Risk provisions for CEE operations meanwhile were 54% of regional net income. 2009 was a year of negative GDP growth across the entire region of operation, ranging from a 15.1% GDP drop in the Ukraine to a 3% GDP drop in Serbia. In Croatia, direct investments and gross capital investments have also declined yearafter year. Direct investments have declined by a full 30% in 2009, as the flow of investment from the core EU dried up. 12 In 2010, recovery was uneven across the region, with Croatia and Romania experiencing negative GDP growth, while Slovakia experienced a 4.1% GDP growth. Overall, exports are a main driver of GDP growth in the region, while government expenditures declines yearafter year. In comparison, the German GDP saw a 1% increase during 2008 and a 4.7% decrease as Germany reduced its spending in 2009. In 2010, German GDP growth recovered to 3.7%, the highest GDP growth figure since reunification. 13 The Eurozone GDP meanwhile, grew at 0.4% in 2008 and suffered a 4.1% GDP contraction in 2009. The following year brought 1.7% GDP growth. In 2010, the also Eurozone experienced a strong recovery of industrial production, with capital goods experiences doubledigit growth rates.14 According to Volksbank’s 2010 annual report, the stated business strategy for the CEE region includes a conservative risk strategy and a focus on microenterprise and SME’s.
SOURCES OF VULNERABILITY
Given the structure and exposures of Volksbank, there are a number of potential sources of bank stress. There are both environmental potential sources of bank stress as well as firmspecific idiosyncratic potential sources. Intransparency in the financial markets Poor understanding and management of risks and uncertainties Poor understanding and management of liquidity and characteristics of assets in portfolio
11 12
Ibid Ibid 13 Ibid 14 Ibid
20  P a g e
Underlying the 2008 financial crisis was increased intransparency in the global financial markets in relation to the growth of financial innovation during the period immediately preceding the financial crisis. This intransparency was the result of financial innovation outpacing the growth in accompanying regulation aimed at ensuring best practices. In fact, regulation actually shrank during this period, as did financial regulatory enforcement capability. The resulting financial environment was one in which gaps and lags in general understanding of risk and uncertainty in the financial markets were widespread. Another feature of the 2008 financial crisis was immediate onset of chronic illiquidity which ensued once it became clear that the financial models upon which many of the positions and portfolios of many of the major investment banks were built had – for all their complexity – failed to provide a complete understanding of risks and uncertainties in the financial markets. Since 2008, a consensus has emerged regarding generalized misunderstanding of risk and uncertainty in the financial markets reflecting the Keynesian view that there are clear distinctions between uncertainty and risk. The key difference is that we essentially know, understand and can quantify risk. It is a known unknown. Uncertainty on the other hand, is best represented by the idea that we cannot predict perfectly the events of the future. 15 Taleb argues that while the bell curve has made us confident that we have tamed uncertainty, it in fact ignores large and actually cannot handle deviations. 16 Essentially, the mathematical models built based on understanding of the bellcurve become useless in this context. In short, many of the largest players in the financial markets became victim of their unknown unknowns. Lax lending standards and regulations Leverage during a bubble situation Insufficient standards for capital visàvis the positions, portfolios, and activities of the financial and banking sectors
Generally, the development of asset bubbles takes place in a context of massive amounts of leverage and lax lending standards. Furthermore, standards for capital and loss provisions are usually insufficient for the riskiness of the operations of the parties. During the US asset bubble linked to the 2008 financial crisis, lax capital requirements stemmed from an imperfect understanding of the risks, uncertainties, liquidity, and correlation of recentlydeveloped financial derivatives being traded. Interconnectedness/correlation of segments and segmental shocks
Correlation of asset classes is a common characteristic of nearly all financial crises. This undermines the stability and security value of diversified portfolios during such times. In many cases, it is the growth of financial innovation and the nature of ownership structure which actually leads to the interconnectedness of the asset classes.
15 16
Skidelsky, 2009 Taleb, 2007
21  P a g e
Prior to the 2008 financial crisis for instance, (Real Estate Investment Trust) REITs were a common way in which real estate exposure was included in portfolios. This is especially true of exposure to overseas real estate. While real estate investment is typically characterized by its noncorrelation with other asset classes, REITs were known to have high correlation to equity. Currency crisis Collapse of one of the CEE region economies Contagion
The CEE region’s dependence on exportled growth could easily make it susceptible to economic downturns in trading partners. Furthermore, given the CEE region’s highly volatile GDP growth and dependence on the core EU countries as a source of investment, the collapse of an individual economy in the CEE region could occur. Such an event would have farreaching consequences. Due to both dependence on exportdriven growth and the economic interconnectedness of this region, the possibilities for contagion are quite good. Furthermore, the most of the region’s financial investments come from the same sourceWestern Europe. This means that the same asset managers might quickly dump all of their assets in the region once they detected trouble in one country, further exacerbating contagion potential. During the 1998 Asian financial crisis, the collapse of exportgrowthdependent economies came in the form of a localized currency crisis, which then provoked a regional contagion. The central mechanism was one of losses in Thailand due to devaluation of the Thai Baht led to a regionwide selloff of assets by international investors. Furthermore, the Asian financial crisis arose in the aftermath of a regionwide liberalization of foreign portfolio investment regulations and financial capital mobility regulations in the early 1990’s. It is a welldocumented phenomenon that increases in capital mobility have historically been followed by crisis.17 Currency instability in one of the CEE countries would lead to losses in value of localcurrencydenominated assets, leading to their selloff, irrespective of the actual domesticallyweighted performance of said assets. Decline in the retail banking business in home market or in lowvolatility foreign markets.
Any sound bank, business, or portfolio needs solid diversification in order to minimize idiosyncratic risk. Given the size and importance of Volksbank’s retail segment as part of its overall business, it would be safe to assume that potentially more lucrative, higherrisk operations on the periphery of the CEE region are being financed with deposits and income from retail operations in less volatile countries. The deposits and income from the retail segment also serve to maintain the banks liquidity as it invests in peripheral risky projects and maintain solvency in the event of losses stemming from riskier peripheral activities. Thus, the bank’s riskier activities are counterbalanced by a large and regionallydiversified
17
Reinhart and Rogoff, 2008
22  P a g e
retail banking business. A decline in this business would lead to high exposure and higher sensitivity to peripheral volatility, exacerbating the effect of any adverse shocks.
CAPITAL
Volksbank’s regulatory capital can be broken into three elements.18 Tier I: Core Capital consisting of capital reserves, retained earnings, subscribed capital, and hybrid capital less intangible assets. Tier II: Supplementary Capital consisting of noncurrent subordinated liabilities, unrealized trading profits, and risk provisions from lending operations. Amount is limited to 100% of Tier I capital. Tier III: Shortterm (current) Subordinated Liabilities.19 Tier III capital may only be employed to cover market risks.20
In 2010, Austrian capital requirements were slightly in excess of Basel II requirements. Capital requirements in Austria are compared against a riskweighted assessment base, comprised of assets, activities, and positions. Volksbank’s 2010 regulatory riskweighted assessment base was reported at 25, 25.5 Billion EUR. While the minimum credit risk capital requirement was 8%, the Austrian government imposed further capital requirements for both market risk and operational risk. According to Volksbank’s Annual Report, 2010 Tier I capital requirement stood at 2.230 Billion EUR, constituting a core capital requirement ratio of 8.76% of the riskweighted assessment base. Nevertheless, Volksbank’s actual capital stood at 2.612 Billion EUR, constituting a core capital ratio of 9.37%. Volksbank’s 2010 Total Eligible Capital figures showed a total capital buffer of 3.562 Billion EUR, constituting 14% of the bank’s riskweighted assessment base.21
18 19
Volksbank 2010 Ibid 20 Ibid 21 Ibid
23  P a g e
THE STRESS SEQUENCE
A1: A localized currency crisis occurs in the CEE region. Foreigncurrencydenominated assets radically decrease in value. Total assets and annual result before taxes suffer a 10% haircut visàvis 2010 figures. This represents losses caused by exposure to the localized currency crisis, which is assumed to locally penetrate all business segments.
A2: A spike in industrial plant closures occurs in the core Eurozone counties. The macroeffect of the closures in the Eurozone economy is a drop in output, demand, and employment. All of these factors come to have an effect in node B2. The direct and immediate effect of the closures takes the form of: A 5% decrease in corporate segment annual result before taxes, as the flow of projects diminishes. Correspondingly, a 5% increase in real estate annual results stems from commercial space turnover in the real estate segment.
B1: The shock, which was localized in node A1, spreads throughout the CEE region as asset managers rebalance their portfolios, and as there is a generalized selloff of CEE financial assets. GDP growth becomes negative through the region, Germany. While Germany meets the 2011 GDP growth forecast
24  P a g e
outlined in Appendix 2, the rest of the region posts GDP growth figures in line with 2009 results. Net trading income and income from financial investments decline as a result. Net trading income and income from financial investments for the CEE region recede to 2009 levels. These losses are ascribed to the financial markets and investment book segments as outlined in Appendix 3.
B2: The secondary effect of the industrial plant closures in core Eurozone countries for the banking sector overall and specifically for Volksbank, is decline in coreEUbased retail, real estate, and corporate banking. A generalized 20% decline in net interest income, fee and commission income derived from Austrian operations. A 10% decline net interest income, fee and commission income, representing the German market.
C: The combination of downturns in retail and corporate banking in the core Eurozone and the spread of financial crises across Central and Eastern Europe transmit to Western European equity markets. The result is a drop in Western European equity markets. For Volksbank, the effects are multifaceted. Trading and financial income take a haircut avoid going negative. Increases in cost of capital cause liabilities to increase while assets from both the trading book and heldtomaturity segments of Volksbank. Liabilities increase as financing costs increase. Amount owed to credit institutions increases by 15%. Assets of the Trading Book, Financial Market, and Corporate segmental assets fall in value by 20% (this amounts to a 12% decline on total assets). Net income from financial investments becomes zero.
D: Volksbank is forced to deleverage and unwind its positions at a loss in order to attempt to maintain solvency as well as to rebuild its capital buffers and risk provisions. No specific assumptions are made visàvis the makeup of the liquidated assets. Total assets fall by an additional 15%. The losses are proportionally distributed across the segments. Shareholder’s Equity declines by offsetting amount.
25  P a g e
Node Loss/Gain Amount ('000) To/From A1 4,655,567 • Total Assets Total assets and annual result before taxes suffer a 10% haircut visàvis 2010 • Annual result before figures. taxes A2 • A 5% decrease in corporate segment annual result before taxes, as the flow of projects diminishes. • Correspondingly, a 5% increase in real estate annual results stems from commercial space turnover in the real estate segment. Net trading income and income from financial investments for the CEE region recede to 2009 levels. These losses are ascribed to the financial markets and investment book segments • A generalized 20% decline in fee and commission income derived from Austrian operations. • A 10% decline in fee and commission income, representing the German market. 1,360 • Corp. result before taxes • RE result before taxes 15,125 • Net Trading Income • Income from Fin. Inv. 23,735 • Fee and commission income
B1
B2
C
• Liabilities increase as financing costs increase. Amount owed to credit institutions increases by 15%. • Assets of the Trading Book, Financial Market, and Corporate segmental assets fall in value by 20% (this amounts to a 12% decline on total assets). • Net income from financial investments becomes zero. Total assets fall by an additional 15%. The losses are proportionally distributed across the segments.
6,329,424 • Amount owed to credit institutions • Assets • Annual result before taxes 5,652,794 Total Assets
D
THE BAYESIAN NET
26  P a g e
Conclusion
The intention of this research is to suggest improvements to commonlyused stresstesting methodologies in order to address what have been identified as methodological shortcomings, particularly in the European context, wherein the European Commission has already expressed dissatisfaction with both the 2009 and the 2010 stress tests. The key missing ingredient from stress testing procedures is a solid way of capturing the corelationships between the various stress events. The corelationship between the stress events should be assigned by experts, and informed by as broad a range as possible of indicators or future occurrences, events, and vulnerabilities. By finding such a way, we would be able to generate statistical information about stress losses that are more realistic than losses captured by conventional stress testing methodologies. The Bayesian net approach to stress testing proposed here is based on the work of Dr. Rebonato. We developed a teaching tool based on this material in the form of a Powerpoint presentation, with contents that should take one workshop day to cover. In addition, we developed a program written in Excel and Visual Basic for Applications (VBA) that allows a risk manager to enter a Bayesian scenario with a structure of choice and accompanying subjective probabilities and potential losses. The program checks the entered data for consistency, and then generates the full joint probability table and an accompanying conditional losses table that allows the risk manager to assign capital positions to his scenario. Finally, we developed a realistic case that demonstrates the usefulness of the approach to potential clients with the example of Volksbank. The scenario which we construct for Volksbank is a multistage stress scenario. Essentially, it is a metastress scenario. The subjective probabilities in the scenario are based on, as well as drawn from the sources of potential vulnerability included in the scenario analysis. In creating these contents, we had to revise some points from the practical procedure of resolving a Bayesian net in the book that turned out to be problematic. We feel that our work is a useful addition to the book. What we suggest in this paper is a viable, more complete and comprehensive way of stress testing which can be easily implemented by banks and regulators in order to cope with the causality and correlation among economic shocks. The step towards a more complete methodology for stress testing, which is both updating and includes forwardlooking information is necessary going into the future. If we have learned anything from the 1998 Asian financial crisis and the 2008 global financial crisis, it is that economics shocks are often correlated and that a severe crisis comes in stages.
27  P a g e
References
Basel Committee on Banking Supervision, “Principles for Sound Stress Testing and Supervision”, Consultative Document, Basel, January, 2009 Bloomber Businessweek http://investing.businessweek.com/research/stocks/private/snapshot.asp?privcapId=875308 De Jonghe, Frank, “Financial Risk Management”, Lecture Series, Ghent University, 2010 De Jonghe, Frank and Nikolas Vander Vennet, “Some New Approaches to Stress Testing and Scenarios Analysis”, Deloitte, 2011 Rebonato, Ricardo “Coherent Stress Testing: A Bayesian Approach to the Analysis of Financial Risk” 2010 Reinhart and Rogoff, “This Time it’s Different: A Panoramic View of Eight Centuries of Financial Crises” NBER, 2008 Skidelsky, Robert “Keynes, Return of the Master” Allen Lane, 2009 Taleb, Nassim, “The Black Swan: The impact of the Highly Improbable” Allen Lane, 2007 Volksbank, “2010 Annual Report”, Vienna, 2010
28  P a g e
Appendix 1
Regulatory Own Resources and Capital
29  P a g e
RiskWeighted Assessment Base and Minimum Capital Requirement
Tier 1 Issues
30  P a g e
Tier 2 Issues
31  P a g e
32  P a g e
Appendix 2
2010 Volksbank segmental breakdown taken form page 156 of Volksbank’s 2010 Annual Report
33  P a g e
Appendix 3 2010 Volksbank regional breakdown, domestic vs. foreign markets, taken from page 157 of Volksbank’s 2010 Annual Report
34  P a g e
Appendix 4
2010 Income Statement, taken from page 98 of Volksbank’s Annual Report
35  P a g e
Appendix 5
Volksbank Statement of Financial Position (Balance Sheet), taken from page 99 of 2010 Annual Report
36  P a g e
Appendix 6
Economic Growth outlooks and construction activity outlook in the CEE region, taken from pages 34 and 48 of the 2010 Volksbank Annual Report
37  P a g e
Appendix 7
Visual Basic for Applications code of the program
Option Explicit '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ' Author: Kevin Hoefman, 22 June 2011, contact: kevin.hoefman@howest.be ' Notes: '  I am using onebased array counting throughout this program. In a variable Values(4), I will use Values(1), Values(2), Values(3) and Values(4) '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' Sub CheckTopology() 'Select Topology range and dimensions Dim Topology As Range Set Topology = Worksheets(1).Cells(1, 1).CurrentRegion Dim Message As String CheckTopology_Range Topology, Message MsgBox (Message) End Sub Function CheckTopology_Range(ByVal MyRange As Range, ByRef Message As String) As Boolean Dim RowCount, ColumnCount RowCount = MyRange.Rows.Count ColumnCount = MyRange.Columns.Count 'Check matrix symmetry, abort if not If RowCount <> ColumnCount Then Message = "Fail: Matrix is not symmetric" CheckTopology_Range = False Exit Function End If 'Check if cells are filled in properly, abort if not If Trim(MyRange.Cells(1, 1)) <> "" Then MsgBox ("Fail: Improperly formed topology  Top left cell should be empty") CheckTopology_Range = False Exit Function End If Dim Count For Count = 2 To RowCount If Trim(MyRange.Cells(1, Count)) <> Trim(MyRange.Cells(Count, 1)) Then Message = "Fail: Improperly formed topology  Column titles don't match row titles" CheckTopology_Range = False Exit Function End If Next Dim SubTopology As Range Set SubTopology = Range(MyRange.Cells(2, 2), MyRange.Cells(RowCount, ColumnCount)) Dim Cell For Each Cell In SubTopology If Trim(Cell) <> "0" And Trim(Cell) <> "1" Then Message = "Fail: Improperly formed topology  At least one inner field is neither 0 nor 1" CheckTopology_Range = False Exit Function End If Next 'If all was successful, report dimension of topology Message = "This is a " & (RowCount  1) & " event topology" CheckTopology_Range = True End Function
38  P a g e
Sub GenerateJointProbabilities() 'Select Topology range Dim Topology As Range Set Topology = Worksheets(1).Cells(1, 1).CurrentRegion 'Select SubTopology range Dim SubTopology As Range Set SubTopology = Range(Topology.Cells(3, 2), Topology.Cells(Topology.Rows.Count, Topology.Columns.Count  1)) 'Select full sheet Dim FullRange As Range Set FullRange = Worksheets(1).UsedRange 'Check the Topology just to be sure TODO: checktopology hier insteken Dim Message As String If CheckTopology_Range(Topology, Message) = False Then MsgBox ("Malformed topology  use the CheckTopology button for more information") Exit Sub End If 'Extract the event names from the topology Dim EventNames() As String ExtractEventNames Topology, EventNames 'Create arrays to hold marginal probabilities, single conditioned probabilities and double conditioned probabilities ReDim MarginalProbArr(UBound(EventNames)) As MarginalProb ReDim SingleCondProbArr(1) As SingleCondProb ' will be redimmed ReDim DoubleCondProbArr(1) As DoubleCondProb ' will be redimmed 'Extract required marginal probabilities and report missing values if any, exit function if missing values If ExtractMarginalProbabilities(EventNames, MarginalProbArr, FullRange) = False Then Exit Sub 'Extract required single conditioned probabilities If ExtractSingleConditionedProbs(EventNames, SingleCondProbArr, SubTopology, FullRange, MarginalProbArr) = False Then Exit Su b 'Extract required double conditioned probabilities and their corresponding extra single conditioned probability If ExtractDoubleConditionedProbs(EventNames, DoubleCondProbArr, SubTopology, FullRange, MarginalProbArr, SingleCondProbArr) = False Then Exit Sub 'Do Sanity Checks If CheckSanity(MarginalProbArr, SingleCondProbArr, DoubleCondProbArr) = False Then Exit Sub 'Determine the functional form of the topology ReDim Functional_MarginalProbArr(1) As MarginalProb ReDim Functional_SingleCondProbArr(1) As SingleCondProb Dim SingleConditioned_Count, Marginal_Count, CountSingle, CountMarginal, CountDouble As Integer SingleConditioned_Count = 0 'Extract the functional single conditioned probability nodes For CountSingle = 1 To UBound(SingleCondProbArr) If SingleCondProbArr(CountSingle).IsFunctional() = True Then SingleConditioned_Count = SingleConditioned_Count + 1 Next ReDim Functional_SingleCondProbArr(SingleConditioned_Count) SingleConditioned_Count = 1 For CountSingle = 1 To UBound(SingleCondProbArr) If SingleCondProbArr(CountSingle).IsFunctional() = True Then Set Functional_SingleCondProbArr(SingleConditioned_Count) = SingleCondProbArr(CountSingle) SingleConditioned_Count = SingleConditioned_Count + 1 End If Next 'Determine functional top nodes If UBound(EventNames) = 1 Then MsgBox ("Topology is of only 1 dimension, not much to be done here... exiting.") Exit Sub Else Dim CountEmptyRows, CountOnes, CountColumns As Integer
39  P a g e
Dim IsEmptyRow As Boolean CountEmptyRows = 0 IsEmptyRow = True Do While IsEmptyRow = True And CountEmptyRows + 3 <= Topology.Rows.Count CountOnes = 0 For CountColumns = 1 To UBound(EventNames) + 1 If Topology.Cells(3 + CountEmptyRows, CountColumns) = 1 Then CountOnes = CountOnes + 1 If CountOnes > 1 Then IsEmptyRow = False Next If CountOnes = 1 Then CountEmptyRows = CountEmptyRows + 1 Loop 'Number of top nodes is 1 more than the number of empty rows ReDim Functional_MarginalProbArr(CountEmptyRows + 1) For CountMarginal = 1 To CountEmptyRows + 1 Set Functional_MarginalProbArr(CountMarginal) = MarginalProbArr(CountMarginal) Next End If 'Locate the target worksheet Dim ResultsSheet As Worksheet Set ResultsSheet = Worksheets(2) 'Clear the results sheet ResultsSheet.UsedRange.Clear 'Center the range, set titles to desired style, make each column autofit, set the number formats Dim BottomRow As Integer BottomRow = 1 + Power(2, UBound(EventNames)) Range(ResultsSheet.Cells(1, 1), ResultsSheet.Cells(BottomRow, UBound(EventNames))).HorizontalAlignment = xlCenter Range(ResultsSheet.Cells(1, 1), ResultsSheet.Cells(1, 3 + UBound(EventNames))).HorizontalAlignment = xlCenter Range(ResultsSheet.Cells(1, 1), ResultsSheet.Cells(1, 3 + UBound(EventNames))).Font.Bold = True Dim Cell As Range For Each Cell In Range(ResultsSheet.Cells(1, 1), ResultsSheet.Cells(1, 3 + UBound(EventNames))) Cell.EntireColumn.AutoFit Next Cell Range(ResultsSheet.Cells(2, 1 + UBound(EventNames)), ResultsSheet.Cells(BottomRow, 1 + UBound(EventNames))).NumberFormat = "0.00000000 " Range(ResultsSheet.Cells(2, 2 + UBound(EventNames)), ResultsSheet.Cells(BottomRow, 3 + UBound(EventNames))).NumberFormat = "###,###,###,###,##0.00 " 'Fill in Titles FillInTitles EventNames, ResultsSheet 'Write other table titles Dim TitleCount As Integer Dim Title As String Title = "P(" For TitleCount = 1 To UBound(EventNames)  1 Title = Title & EventNames(TitleCount) & ", " Next Title = Title & EventNames(TitleCount) & ")" ResultsSheet.Cells(1, UBound(EventNames) + 1) = Title ResultsSheet.Cells(1, UBound(EventNames) + 2) = "Gain/Loss" ResultsSheet.Cells(1, UBound(EventNames) + 3) = "Conditional Gain/Loss" 'Write Zeros and Ones FillInZerosAndOnes EventNames, ResultsSheet 'Calculate and write the joint probabilities CalculateJointProbabilities EventNames, Functional_MarginalProbArr, Functional_SingleCondProbArr, DoubleCondProbArr, ResultsSheet 'Calculate gain/losses
40  P a g e
CalculateGainLosses MarginalProbArr, ResultsSheet 'Write the capital Dim CapitalCell As Range Set CapitalCell = ResultsSheet.Cells(2 + BottomRow, UBound(EventNames) + 3) CapitalCell.Formula = "=Min(" & ResultsSheet.Cells(2, 3 + UBound(EventNames)).Address & ":" & ResultsSheet.Cells(BottomRow, 3 + UBound(EventNames)).Address & ") * 1" CapitalCell.NumberFormat = "###,###,###,###,##0.00 " CapitalCell.Font.Bold = True CapitalCell.Offset(0, 1) = "Capital:" CapitalCell.Offset(0, 1).Font.Bold = True CapitalCell.Offset(0, 1).HorizontalAlignment = xlRight Range(CapitalCell.Offset(0, 1), CapitalCell).BorderAround xlContinuous, xlThick, xlColorIndexAutomatic 'Set results sheet active ResultsSheet.Activate End Sub Sub ExtractEventNames(ByVal MyRange As Range, ByRef EventNames() As String) 'Select Topology range and dimensions Dim RowCount, ColumnCount RowCount = MyRange.Rows.Count ColumnCount = MyRange.Columns.Count 'Check matrix symmetry, abort if not If RowCount <> ColumnCount Then MsgBox ("Fail: Matrix is not symmetric") Exit Sub End If ReDim EventNames(RowCount  1) Dim Count For Count = 2 To RowCount EventNames(Count  1) = MyRange.Cells(Count, 1) Next End Sub Function ExtractMarginalProbabilities(ByRef EventNames() As String, ByRef MarginalProbArr() As MarginalProb, ByRef FullRange As Range) As Boolean 'Create the necessary amount of objects Dim Count As Integer For Count = 1 To UBound(EventNames) Set MarginalProbArr(Count) = New MarginalProb MarginalProbArr(Count).Create (EventNames(Count)) Next 'Find their values and exit with either true if all values are found or false if not ExtractMarginalProbabilities = ScanRangeForMarginalProbs(MarginalProbArr, FullRange) End Function Function ScanRangeForMarginalProbs(ByRef MarginalProbArr() As MarginalProb, ByRef FullRange As Range) Dim MessageMissing As String Dim CountMarginals, CountFound, RowCount As Integer Dim Found As Boolean CountFound = 0 For CountMarginals = 1 To UBound(MarginalProbArr) Found = False RowCount = 1 Do While RowCount <= FullRange.Rows.Count And Found = False If Replace(FullRange.Cells(RowCount, 1), " ", "") = MarginalProbArr(CountMarginals).Name() Then MarginalProbArr(CountMarginals).CreateValues FullRange.Cells(RowCount, 2), FullRange.Cells(RowCount, 3) Found = True End If RowCount = RowCount + 1
41  P a g e
Loop If Found = False Then MessageMissing = MessageMissing & vbCrLf & MarginalProbArr(CountMarginals).Name Else CountFound = CountFound + 1 End If Next If CountFound = UBound(MarginalProbArr) Then ScanRangeForMarginalProbs = True Else MsgBox ("Missing marginal probabilities:" & MessageMissing) ScanRangeForMarginalProbs = False End If End Function Function ExtractSingleConditionedProbs(ByRef EventNames() As String, ByRef SingleCondProbArr() As SingleCondProb, ByRef SubTopology As Range, ByRef FullRange As Range, ByRef MarginalProbArr() As MarginalProb) As Boolean 'Determine necessary space in array Dim ConditionalCount As Integer ConditionalCount = 0 Dim CountRows, CountColumns As Integer For CountRows = 1 To SubTopology.Rows.Count For CountColumns = 1 To CountRows If SubTopology.Cells(CountRows, CountColumns) = 1 Then ConditionalCount = ConditionalCount + 1 Next Next 'Resize array ReDim SingleCondProbArr(ConditionalCount) 'Create the objects Dim Position As Integer Position = 1 For CountRows = 1 To SubTopology.Rows.Count For CountColumns = 1 To CountRows If SubTopology.Cells(CountRows, CountColumns) = 1 Then Set SingleCondProbArr(Position) = New SingleCondProb SingleCondProbArr(Position).Create EventNames(CountRows + 1), EventNames(CountColumns) Position = Position + 1 End If Next Next 'Find their values and exit with either true if all values are found or false if not ExtractSingleConditionedProbs = ScanRangeForSinglecondProbs(SingleCondProbArr, FullRange, MarginalProbArr) End Function Function ScanRangeForSinglecondProbs(ByRef SingleCondProbArr() As SingleCondProb, ByRef FullRange As Range, ByRef MarginalProbArr() As MarginalProb) Dim MessageMissing As String Dim CountSingleConds, CountMarginals, CountFound, RowCount, PositionChild, PositionParent As Integer Dim Found As Boolean CountFound = 0 For CountSingleConds = 1 To UBound(SingleCondProbArr) Found = False RowCount = 1 Do While RowCount <= FullRange.Rows.Count And Found = False If Replace(FullRange.Cells(RowCount, 1), " ", "") = SingleCondProbArr(CountSingleConds).Name() Then 'find the corresponding marginal probabilities For CountMarginals = 1 To UBound(MarginalProbArr) If MarginalProbArr(CountMarginals).Node = SingleCondProbArr(CountSingleConds).ChildNode Then PositionChild = CountMarginals If MarginalProbArr(CountMarginals).Node = SingleCondProbArr(CountSingleConds).ParentNode Then PositionParent = CountMarginals
42  P a g e
Next SingleCondProbArr(CountSingleConds).CreateValues FullRange.Cells(RowCount, 2), MarginalProbArr(PositionChild), MarginalProbArr(PositionParent) Found = True End If RowCount = RowCount + 1 Loop If Found = False Then MessageMissing = MessageMissing & vbCrLf & SingleCondProbArr(CountSingleConds).Name Else CountFound = CountFound + 1 End If Next If CountFound = UBound(SingleCondProbArr) Then ScanRangeForSinglecondProbs = True Else MsgBox ("Missing singleconditional probabilities:" & MessageMissing) ScanRangeForSinglecondProbs = False End If End Function Function ExtractDoubleConditionedProbs(ByRef EventNames() As String, ByRef DoubleCondProbArr() As DoubleCondProb, ByRef SubTopology As Range, ByRef FullRange As Range, ByRef MarginalProbArr() As MarginalProb, ByRef SingleCondProbArr() As SingleCondProb) As Boolean 'Determine necessary space in array Dim DoubleConditionalCount As Integer DoubleConditionalCount = 0 Dim CountOnes As Integer Dim CountRows, CountColumns As Integer For CountRows = 1 To SubTopology.Rows.Count CountOnes = 0 For CountColumns = 1 To CountRows If SubTopology.Cells(CountRows, CountColumns) = 1 Then CountOnes = CountOnes + 1 Next If CountOnes = 2 Then DoubleConditionalCount = DoubleConditionalCount + 1 Next 'Resize array ReDim DoubleCondProbArr(DoubleConditionalCount) 'Create array for corresponding single conditionals ReDim DoubleCondBridgeArr(DoubleConditionalCount) As SingleCondProb 'Create the objects Dim Position As Integer Position = 1 Dim PositionOfOnes(2) As Integer For CountRows = 1 To SubTopology.Rows.Count CountOnes = 0 For CountColumns = 1 To CountRows If SubTopology.Cells(CountRows, CountColumns) = 1 Then CountOnes = CountOnes + 1 PositionOfOnes(CountOnes) = CountColumns End If Next If CountOnes = 2 Then 'double conditioned probability found Set DoubleCondProbArr(Position) = New DoubleCondProb DoubleCondProbArr(Position).Create EventNames(CountRows + 1), EventNames(PositionOfOnes(1)), EventNames(PositionOfOnes(2)) Set DoubleCondBridgeArr(Position) = New SingleCondProb DoubleCondBridgeArr(Position).Create EventNames(PositionOfOnes(1)), EventNames(PositionOfOnes(2)) Position = Position + 1 End If
43  P a g e
Next 'Find their values and exit with either true if all values are found or false if not ExtractDoubleConditionedProbs = ScanRangeForDoublecondProbs(DoubleCondProbArr, DoubleCondBridgeArr, FullRange, SingleCondProbArr, MarginalProbArr) End Function Function ScanRangeForDoublecondProbs(ByRef DoubleCondProbArr() As DoubleCondProb, ByRef DoubleCondBridgeArr() As SingleCondProb, ByRef FullRange As Range, ByRef SingleCondProbArr() As SingleCondProb, ByRef MarginalProbArr() As MarginalProb) As Boolean Dim MessageMissing As String Dim CountDoubleConds, CountMarginals, CountSingleConds, CountFound, RowCount, PositionChild, PositionParentLeft, PositionParentRight, PositionSingleLeft, PositionSingleRight As Integer Dim Found As Boolean CountFound = 0 'First, find the bridge single conditional probabilities, abort if not found If ScanRangeForSinglecondProbs(DoubleCondBridgeArr, FullRange, MarginalProbArr) = False Then ScanRangeForDoublecondProbs = False Exit Function End If 'If found, find the double conditional probabilities For CountDoubleConds = 1 To UBound(DoubleCondProbArr) Found = False RowCount = 1 Do While RowCount <= FullRange.Rows.Count And Found = False If Replace(FullRange.Cells(RowCount, 1), " ", "") = DoubleCondProbArr(CountDoubleConds).Name() Then 'find the corresponding marginal probabilities For CountMarginals = 1 To UBound(MarginalProbArr) If MarginalProbArr(CountMarginals).Node = DoubleCondProbArr(CountDoubleConds).ChildNode Then PositionChild = CountMarginals If MarginalProbArr(CountMarginals).Node = DoubleCondProbArr(CountDoubleConds).ParentNodeLeft Then PositionParentLeft = CountMarginals If MarginalProbArr(CountMarginals).Node = DoubleCondProbArr(CountDoubleConds).ParentNodeRight Then PositionParentRight = CountMarginals Next 'find the corresponding single conditioned probabilities For CountSingleConds = 1 To UBound(SingleCondProbArr) If SingleCondProbArr(CountSingleConds).ChildNode = DoubleCondProbArr(CountDoubleConds).ChildNode And SingleCondProbArr(CountSingleConds).ParentNode = DoubleCondProbArr(CountDoubleConds).ParentNodeLeft Then PositionSingleLeft = CountSingleConds If SingleCondProbArr(CountSingleConds).ChildNode = DoubleCondProbArr(CountDoubleConds).ChildNode And SingleCondProbArr(CountSingleConds).ParentNode = DoubleCondProbArr(CountDoubleConds).ParentNodeRight Then PositionSingleRight = CountSingleConds Next DoubleCondProbArr(CountDoubleConds).CreateValues FullRange.Cells(RowCount, 2), MarginalProbArr(PositionChild), MarginalProbArr(PositionParentLeft), MarginalProbArr(PositionParentRight), _ SingleCondProbArr(PositionSingleLeft), SingleCondProbArr(PositionSingleRight), DoubleCondBridgeArr(CountDoubleConds) Found = True End If RowCount = RowCount + 1 Loop If Found = False Then MessageMissing = MessageMissing & vbCrLf & DoubleCondProbArr(CountDoubleConds).Name Else CountFound = CountFound + 1 End If Next If CountFound = UBound(DoubleCondProbArr) Then ScanRangeForDoublecondProbs = True Else MsgBox ("Missing doubleconditional probabilities:" & MessageMissing) ScanRangeForDoublecondProbs = False
44  P a g e
End If End Function Sub CalculateJointProbabilities(ByRef EventNames() As String, ByRef Functional_MarginalProbArr() As MarginalProb, ByRef Functional_SingleCondProbArr() As SingleCondProb, ByRef DoubleCondProbArr() As DoubleCondProb, ByRef ResultsSheet As Worksheet) ReDim States(UBound(EventNames)) As Boolean Dim CountRows, CountColumns, CountMarginals, CountSingles, CountDoubles As Integer Dim Result As Double 'Do for every row For CountRows = 2 To ResultsSheet.UsedRange.Rows.Count 'Determine the states that correspond with this row For CountColumns = 1 To UBound(EventNames) If ResultsSheet.Cells(CountRows, CountColumns) = "1" Then States(CountColumns) = True Else States(CountColumns) = False End If Next 'Calculate the joint probability Result = 1 For CountMarginals = 1 To UBound(Functional_MarginalProbArr) Result = Result * Functional_MarginalProbArr(CountMarginals).GetValueByStates(EventNames, States) Next For CountSingles = 1 To UBound(Functional_SingleCondProbArr) Result = Result * Functional_SingleCondProbArr(CountSingles).GetValueByStates(EventNames, States) Next For CountDoubles = 1 To UBound(DoubleCondProbArr) Result = Result * DoubleCondProbArr(CountDoubles).GetValueByStates(EventNames, States) Next 'Write it to the sheet ResultsSheet.Cells(CountRows, UBound(EventNames) + 1) = Result Next End Sub Sub CalculateGainLosses(ByRef MarginalProbArr() As MarginalProb, ByRef ResultsSheet As Worksheet) Dim CountRows, CountColumns, CountMarginals As Integer Dim Result As Double 'Do for every row For CountRows = 2 To ResultsSheet.UsedRange.Rows.Count 'Calculate the total gain/loss for this row Result = 0 For CountColumns = 1 To UBound(MarginalProbArr) Result = Result + ResultsSheet.Cells(CountRows, CountColumns) * MarginalProbArr(CountColumns).GainLoss Next 'Write it to the sheet ResultsSheet.Cells(CountRows, UBound(MarginalProbArr) + 2) = Result 'Write the conditional result to the sheet ResultsSheet.Cells(CountRows, UBound(MarginalProbArr) + 3) = ResultsSheet.Cells(CountRows, UBound(MarginalProbArr) + 1) * Result Next End Sub Sub PrintSingleCondProb(ByRef MySingleCondProb As SingleCondProb, ByRef Nodes() As String) Dim Message As String Message = MySingleCondProb.Name Dim States(2) As Boolean States(1) = True States(2) = True Message = Message & vbCrLf & Nodes(1) & "=" & States(1) & ", " & Nodes(2) & "=" & States(2) & ": " & MySingleCondProb.GetValueByStates(Nodes, States)
45  P a g e
States(1) = True States(2) = False Message = Message & vbCrLf & Nodes(1) & "=" & States(1) & ", " & Nodes(2) & "=" & States(2) & ": " & MySingleCondProb.GetValueByStates(Nodes, States) States(1) = False States(2) = True Message = Message & vbCrLf & Nodes(1) & "=" & States(1) & ", " & Nodes(2) & "=" & States(2) & ": " & MySingleCondProb.GetValueByStates(Nodes, States) States(1) = False States(2) = False Message = Message & vbCrLf & Nodes(1) & "=" & States(1) & ", " & Nodes(2) & "=" & States(2) & ": " & MySingleCondProb.GetValueByStates(Nodes, States) MsgBox (Message) End Sub Sub PrintDoubleCondProb(ByRef MyDoubleCondProb As DoubleCondProb, ByRef Nodes() As String) Dim Message As String Message = MyDoubleCondProb.Name Dim States(3) As Boolean States(1) = True States(2) = True States(3) = True Message = Message & vbCrLf & Nodes(1) & "=" & States(1) & ", " & Nodes(2) & "=" & States(2) & ", " & Nodes(3) & "=" & States(3) & ": " & MyDoubleCondProb.GetValueByStates(Nodes, States) States(1) = True States(2) = True States(3) = False Message = Message & vbCrLf & Nodes(1) & "=" & States(1) & ", " & Nodes(2) & "=" & States(2) & ", " & Nodes(3) & "=" & States(3) & ": " & MyDoubleCondProb.GetValueByStates(Nodes, States) States(1) = True States(2) = False States(3) = True Message = Message & vbCrLf & Nodes(1) & "=" & States(1) & ", " & Nodes(2) & "=" & States(2) & ", " & Nodes(3) & "=" & States(3) & ": " & MyDoubleCondProb.GetValueByStates(Nodes, States) States(1) = True States(2) = False States(3) = False Message = Message & vbCrLf & Nodes(1) & "=" & States(1) & ", " & Nodes(2) & "=" & States(2) & ", " & Nodes(3) & "=" & States(3) & ": " & MyDoubleCondProb.GetValueByStates(Nodes, States) States(1) = False States(2) = True States(3) = True Message = Message & vbCrLf & Nodes(1) & "=" & States(1) & ", " & Nodes(2) & "=" & States(2) & ", " & Nodes(3) & "=" & States(3) & ": " & MyDoubleCondProb.GetValueByStates(Nodes, States) States(1) = False States(2) = True States(3) = False Message = Message & vbCrLf & Nodes(1) & "=" & States(1) & ", " & Nodes(2) & "=" & States(2) & ", " & Nodes(3) & "=" & States(3) & ": " & MyDoubleCondProb.GetValueByStates(Nodes, States) States(1) = False States(2) = False States(3) = True Message = Message & vbCrLf & Nodes(1) & "=" & States(1) & ", " & Nodes(2) & "=" & States(2) & ", " & Nodes(3) & "=" & States(3) & ": " & MyDoubleCondProb.GetValueByStates(Nodes, States) States(1) = False States(2) = False States(3) = False Message = Message & vbCrLf & Nodes(1) & "=" & States(1) & ", " & Nodes(2) & "=" & States(2) & ", " & Nodes(3) & "=" & States(3) & ": " & MyDoubleCondProb.GetValueByStates(Nodes, States) MsgBox (Message) End Sub Sub FillInTitles(ByRef EventNames() As String, ByRef ResultsSheet As Worksheet) Dim NamesCount For NamesCount = 1 To UBound(EventNames)
46  P a g e
ResultsSheet.Cells(1, NamesCount) = EventNames(NamesCount) Next End Sub Sub FillInZerosAndOnes(ByRef EventNames() As String, ByRef ResultsSheet As Worksheet) Dim ColumnCount, IterationCount, WriteCount, RowCount As Integer For ColumnCount = UBound(EventNames) To 1 Step 1 RowCount = 2 For IterationCount = 1 To Power(2, ColumnCount  1) For WriteCount = 1 To Power(2, UBound(EventNames)  ColumnCount) ResultsSheet.Cells(RowCount, ColumnCount) = "1" RowCount = RowCount + 1 Next For WriteCount = 1 To Power(2, UBound(EventNames)  ColumnCount) ResultsSheet.Cells(RowCount, ColumnCount) = "0" RowCount = RowCount + 1 Next Next Next End Sub Function Power(ByRef Number As Integer, ByRef Pow As Integer) If Pow = 0 Then Power = 1 Exit Function End If Dim PowCount As Integer Dim Result As Integer Result = 1 For PowCount = 1 To Pow Result = Result * Number Next Power = Result End Function Function CheckSanity(ByRef MarginalProbArr() As MarginalProb, ByRef SingleCondProbArr() As SingleCondProb, ByRef DoubleCondPr obArr() As DoubleCondProb) As Boolean Dim Message As String Dim IsSane As Boolean IsSane = True Dim CountMarginal, CountSingle, CountDouble As Integer For CountMarginal = 1 To UBound(MarginalProbArr) If MarginalProbArr(CountMarginal).CheckSanity(Message) = False Then IsSane = False Next For CountSingle = 1 To UBound(SingleCondProbArr) If SingleCondProbArr(CountSingle).CheckSanity(Message) = False Then IsSane = False Next For CountDouble = 1 To UBound(DoubleCondProbArr) If DoubleCondProbArr(CountDouble).CheckSanity(Message) = False Then IsSane = False Next If IsSane = False Then MsgBox ("Performing sanity check..." & vbCrLf & vbCrLf & "Sanity check failed." & Message & vbCrLf & vbCrLf & "Aborting proce dure.") CheckSanity = False Else MsgBox ("Performing sanity check..." & vbCrLf & "Sanity check succeeded.") CheckSanity = True End If End Function Sub UnitTest_MarginalProb() Dim MargA As MarginalProb Set MargA = New MarginalProb
47  P a g e
MargA.Create ("A") MargA.CreateValues (0.01) MsgBox (MargA.Name & ":" & vbCrLf & MargA.GetValue(True) & vbCrLf & MargA.GetValue(False)) Dim Nodes(3) As String Nodes(1) = "D" Nodes(2) = "C" Nodes(3) = "A" Dim States(3) As Boolean States(1) = True States(2) = True States(3) = True Dim ATrue, AFalse As Double ATrue = MargA.GetValueByStates(Nodes, States) States(3) = False AFalse = MargA.GetValueByStates(Nodes, States) MsgBox (MargA.Name & ":" & vbCrLf & ATrue & vbCrLf & AFalse) Nodes(3) = "E" ATrue = MargA.GetValueByStates(Nodes, States) End Sub Sub UnitTest_SingleCondProb() Dim MargA As MarginalProb Set MargA = New MarginalProb MargA.Create ("A") MargA.CreateValues (0.01) Dim MargD As MarginalProb Set MargD = New MarginalProb MargD.Create ("D") MargD.CreateValues (0.015) Dim P_D_A As SingleCondProb Set P_D_A = New SingleCondProb P_D_A.Create "D", "A" P_D_A.CreateValues 0.6, MargD, MargA MsgBox (P_D_A.Name & ":" & vbCrLf & P_D_A.GetValue(True, True) & vbCrLf & P_D_A.GetValue(True, False) & vbCrLf & P_D_A.GetValue(False, True) & vbCrLf & P_D_A.GetValue(False, False)) Dim Nodes(3) As String Nodes(1) = "C" Nodes(2) = "D" Nodes(3) = "A" Dim States(3) As Boolean States(1) = True Dim DATrueTrue, DATrueFalse, DAFalseTrue, DAFalseFalse As Double States(2) = True States(3) = True DATrueTrue = P_D_A.GetValueByStates(Nodes, States) States(2) = True States(3) = False DATrueFalse = P_D_A.GetValueByStates(Nodes, States) States(2) = False States(3) = True DAFalseTrue = P_D_A.GetValueByStates(Nodes, States) States(2) = False States(3) = False DAFalseFalse = P_D_A.GetValueByStates(Nodes, States)
48  P a g e
MsgBox (P_D_A.Name & ":" & vbCrLf & DATrueTrue & vbCrLf & DATrueFalse & vbCrLf & DAFalseTrue & vbCrLf & DAFalseFalse) Nodes(2) = "E" Nodes(3) = "F" DAFalseFalse = MargA.GetValueByStates(Nodes, States) End Sub Sub UnitTest_DoubleCondProb() Dim MargD As MarginalProb Set MargD = New MarginalProb MargD.Create ("D") MargD.CreateValues (0.015) Dim MargC As MarginalProb Set MargC = New MarginalProb MargC.Create ("C") MargC.CreateValues (0.02) Dim MargE As MarginalProb Set MargE = New MarginalProb MargE.Create ("E") MargE.CreateValues (0.01) Dim P_E_D As SingleCondProb Set P_E_D = New SingleCondProb P_E_D.Create "E", "D" P_E_D.CreateValues 0.03, MargE, MargD Dim P_E_C As SingleCondProb Set P_E_C = New SingleCondProb P_E_C.Create "E", "C" P_E_C.CreateValues 0.2, MargE, MargC Dim P_D_C As SingleCondProb Set P_D_C = New SingleCondProb P_D_C.Create "D", "C" P_D_C.CreateValues 0.001885, MargD, MargC Dim P_E_DC As DoubleCondProb Set P_E_DC = New DoubleCondProb P_E_DC.Create "E", "D", "C" P_E_DC.CreateValues 0.4, MargE, MargD, MargC, P_E_D, P_E_C, P_D_C MsgBox (P_E_DC.Name & ":" & vbCrLf & P_E_DC.GetValue(True, True, True) & vbCrLf & P_E_DC.GetValue(True, True, False) & vbCrLf & P_E_DC.GetValue(True, False, True) & vbCrLf & P_E_DC.GetValue(True, False, False) & _ vbCrLf & P_E_DC.GetValue(False, True, True) & vbCrLf & P_E_DC.GetValue(False, True, False) & vbCrLf & P_E_DC.GetValue(False, False, True) & vbCrLf & P_E_DC.GetValue(False, False, False)) Dim Nodes(3) As String Nodes(1) = "E" Nodes(2) = "D" Nodes(3) = "C" Dim States(3) As Boolean Dim EDC(8) As Double States(1) = True States(2) = True States(3) = True EDC(1) = P_E_DC.GetValueByStates(Nodes, States) States(1) = True States(2) = True States(3) = False EDC(2) = P_E_DC.GetValueByStates(Nodes, States) States(1) = True
49  P a g e
States(2) = False States(3) = True EDC(3) = P_E_DC.GetValueByStates(Nodes, States) States(1) = True States(2) = False States(3) = False EDC(4) = P_E_DC.GetValueByStates(Nodes, States) States(1) = False States(2) = True States(3) = True EDC(5) = P_E_DC.GetValueByStates(Nodes, States) States(1) = False States(2) = True States(3) = False EDC(6) = P_E_DC.GetValueByStates(Nodes, States) States(1) = False States(2) = False States(3) = True EDC(7) = P_E_DC.GetValueByStates(Nodes, States) States(1) = False States(2) = False States(3) = False EDC(8) = P_E_DC.GetValueByStates(Nodes, States) MsgBox (P_E_DC.Name & ":" & vbCrLf & EDC(1) & vbCrLf & EDC(2) & vbCrLf & EDC(3) & vbCrLf & EDC(4) & _ vbCrLf & EDC(5) & vbCrLf & EDC(6) & vbCrLf & EDC(7) & vbCrLf & EDC(8)) Nodes(1) = "B" Nodes(2) = "C" Nodes(3) = "A" EDC(8) = P_E_DC.GetValueByStates(Nodes, States) End Sub Sub Test_Independence() Dim MargA As MarginalProb Set MargA = New MarginalProb MargA.Create ("A") MargA.CreateValues (0.01) Dim MargB As MarginalProb Set MargB = New MarginalProb MargB.Create ("D") MargB.CreateValues (0.015) Dim P_A_B As SingleCondProb Set P_A_B = New SingleCondProb P_A_B.Create "A", "B" P_A_B.CreateValues MargA.GetValue(True), MargA, MargB MsgBox (P_A_B.Name & ":" & vbCrLf & P_A_B.GetValue(True, True) & vbCrLf & P_A_B.GetValue(True, False) & vbCrLf & P_A_B.GetValue(False, True) & vbCrLf & P_A_B.GetValue(False, False)) End Sub
50  P a g e
Option Explicit '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ' Author: Kevin Hoefman, 22 June 2011, contact: kevin.hoefman@howest.be ' Notes: '  I am using onebased array counting throughout this program. In a variable Values(4), I will use Values(1), Values(2), Values(3) and Values(4) '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' '''''''''''''''''''''' ' Datamembers '''''''''''''''''''''' Private m_Node As String Private m_GainLoss As Double Private m_Values(2) As Double '''''''''''''''''''''' ' Properties '''''''''''''''''''''' Public Property Get Name() As String Name = "P(" & m_Node & ")" End Property Public Property Get Node() As String Node = m_Node End Property Public Property Get GainLoss() As String GainLoss = m_GainLoss End Property '''''''''''''''''''''''''' ' Subroutines & Functions '''''''''''''''''''''''''' Public Sub Create(ByRef Node As String) m_Node = Node End Sub Public Sub CreateValues(ByRef ValueTrue As Double, ByRef GainLoss As Double) m_Values(1) = ValueTrue m_Values(2) = 1  ValueTrue m_GainLoss = GainLoss End Sub Public Function GetValue(ByRef State As Boolean) As Double If State = True Then GetValue = m_Values(1) Else GetValue = m_Values(2) End If End Function Public Function GetValueByStates(ByRef Nodes() As String, ByRef States() As Boolean) As Double Dim Position As Integer Dim Found As Boolean Found = False For Position = 1 To UBound(Nodes) If Nodes(Position) = m_Node Then Found = True Exit For End If Next If Found = False Then MsgBox ("Error finding node: node " & m_Node & " is not found in list of nodes") GetValueByStates = 1 Exit Function
' A positive number means a gain, a negative number means a loss
51  P a g e
End If GetValueByStates = GetValue(States(Position)) End Function Public Function CheckSanity(ByRef Message As String) As Boolean CheckSanity = True If m_Values(1) < 0 Or m_Values(1) >= 1 Then Message = Message & vbCrLf & "Invalid value for " & Name & ": " & m_Values(1) CheckSanity = False End If End Function
Option Explicit '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ' Author: Kevin Hoefman, 22 June 2011, contact: kevin.hoefman@howest.be ' Notes: '  I am using onebased array counting throughout this program. In a variable Values(4), I will use Values(1), Values(2), Values(3) and Values(4) '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' '''''''''''''''''''''' ' Datamembers '''''''''''''''''''''' Private m_ChildNode As String Private m_ParentNode As String Private m_Sanity1Value As Double Private m_Sanity1Message As String Private m_IsFunctional As Boolean Private m_Values(4) As Double ' m_Values(1) = TRUE/TRUE, m_Values(2) = TRUE/FALSE, m_Values(3) = FALSE/TRUE, m_Values(4) = FALSE/FALSE '''''''''''''''''''''' ' Properties '''''''''''''''''''''' Public Property Get Name() As String Name = "P(" & m_ChildNode & "" & m_ParentNode & ")" End Property Public Property Get ChildNode() As String ChildNode = m_ChildNode End Property Public Property Get ParentNode() As String ParentNode = m_ParentNode End Property '''''''''''''''''''''''''' ' Subroutines & Functions '''''''''''''''''''''''''' Public Sub Create(ByRef ChildNode As String, ByRef ParentNode As String) m_ChildNode = ChildNode m_ParentNode = ParentNode m_IsFunctional = True End Sub Public Sub CreateValues(ByRef P_Child_Parent As Double, ByRef MarginalChild As MarginalProb, ByRef MarginalParent As Marginal Prob) m_Values(1) = P_Child_Parent Dim P_Parent_Child As Double P_Parent_Child = P_Child_Parent * MarginalParent.GetValue(True) / MarginalChild.GetValue(True)
52  P a g e
m_Sanity1Value = P_Parent_Child m_Sanity1Message = "Invalid combination of " & Name & ", " & MarginalChild.Name & " and " & MarginalParent.Name & ": P(" & MarginalParent.Node & "" & MarginalChild.Node & ") = " & P_Parent_Child m_Values(2) = (1  P_Parent_Child) * MarginalChild.GetValue(True) / MarginalParent.GetValue(False) m_Values(3) = 1  m_Values(1) m_Values(4) = 1  m_Values(2) End Sub Public Function GetValue(ByRef ChildState As Boolean, ByRef ParentState As Boolean) As Double If ChildState = True And ParentState = True Then GetValue = m_Values(1) ElseIf ChildState = True And ParentState = False Then GetValue = m_Values(2) ElseIf ChildState = False And ParentState = True Then GetValue = m_Values(3) Else GetValue = m_Values(4) End If End Function Public Function GetValueByStates(ByRef Nodes() As String, ByRef States() As Boolean) As Double Dim PositionChild, PositionParent As Integer Dim Found As Boolean 'find the childnode Found = False For PositionChild = 1 To UBound(Nodes) If Nodes(PositionChild) = m_ChildNode Then Found = True Exit For End If Next If Found = False Then MsgBox ("Error finding childnode: childnode " & m_ChildNode & " is not found in list of nodes") GetValueByStates = 1 Exit Function End If 'find the parentnode Found = False For PositionParent = 1 To UBound(Nodes) If Nodes(PositionParent) = m_ParentNode Then Found = True Exit For End If Next If Found = False Then MsgBox ("Error finding parentnode: parentnode " & m_ParentNode & " is not found in list of nodes") GetValueByStates = 1 Exit Function End If 'retrieve the correct value GetValueByStates = GetValue(States(PositionChild), States(PositionParent)) End Function Public Sub SetFunctional(ByRef Value As Boolean) m_IsFunctional = Value End Sub Public Function IsFunctional() As Boolean IsFunctional = m_IsFunctional End Function Public Function CheckSanity(ByRef Message As String) As Boolean
53  P a g e
CheckSanity = True If m_Values(1) < 0 Or m_Values(1) >= 1 Then Message = Message & vbCrLf & "Invalid value for " & Name & ": " & m_Values(1) CheckSanity = False End If If m_Sanity1Value < 0 Or m_Sanity1Value >= 1 Then Message = Message & vbCrLf & m_Sanity1Message CheckSanity = False End If End Function
Option Explicit '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ' Author: Kevin Hoefman, 22 June 2011, contact: kevin.hoefman@howest.be ' Notes: '  I am using onebased array counting throughout this program. In a variable Values(4), I will use Values(1), Values(2), Values(3) and Values(4) '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' '''''''''''''''''''''' ' Datamembers '''''''''''''''''''''' Private m_ChildNode As String ' Nodes: m_ParentNodeLeft m_ParentNodeRight => P(m_ChildNode  m_Par entNodeLeft, m_ParentNodeRight) Private m_ParentNodeLeft As String ' m_ChildNode Private m_Sanity1Value, m_Sanity2Value As Double Private m_Sanity1Message, m_Sanity2Message As String Private m_ParentNodeRight As String ' Note about the ordering of the nodes: mathematically, parent left and parent right can be switched. Program wise the order is important, don't confuse them. Private m_Values(8) As Double ' Values(1) = TRUE/TRUE/TRUE, Values(2) = TRUE/TRUE/FALSE, Values(3) = TRUE/FALSE/TRUE, Values(4) = TRUE/FALSE/FALSE, ' Values(5) = FALSE/TRUE/TRUE, Values(6) = FALSE/TRUE/FALSE, Values(7) = FALSE/FALSE/TRUE, Values(8) = FALSE/FALSE/FALSE, ' in correspondence with the sequence: ' 1 1 1 ' 1 1 0 ' 1 0 1 ' 1 0 0 ' 0 1 1 ' 0 1 0 ' 0 0 1 ' 0 0 0 '''''''''''''''''''''' ' Properties '''''''''''''''''''''' Public Property Get Name() As String Name = "P(" & m_ChildNode & "" & m_ParentNodeLeft & "," & m_ParentNodeRight & ")" End Property Public Property Get ChildNode() As String ChildNode = m_ChildNode End Property Public Property Get ParentNodeLeft() As String ParentNodeLeft = m_ParentNodeLeft End Property Public Property Get ParentNodeRight() As String ParentNodeRight = m_ParentNodeRight End Property
54  P a g e
'''''''''''''''''''''''''' ' Subroutines & Functions '''''''''''''''''''''''''' Public Sub Create(ByRef ChildNode As String, ByRef ParentNodeLeft As String, ByRef ParentNodeRight As String) m_ChildNode = ChildNode m_ParentNodeLeft = ParentNodeLeft m_ParentNodeRight = ParentNodeRight End Sub Public Sub CreateValues(ByRef P_Child_ParentLeft_ParentRight As Double, ByRef Marginal_Child As MarginalProb, ByRef Marginal_ParentLeft As MarginalProb, _ ByRef Marginal_ParentRight As MarginalProb, ByRef SingleCond_Child_ParentLeft As SingleCondProb, _ ByRef SingleCond_Child_ParentRight As SingleCondProb, ByRef SingleCond_ParentLeft_ParentRight As SingleCondProb) m_Values(1) = P_Child_ParentLeft_ParentRight Dim TempReverse As Double 'calculate TRUE TRUE FALSE TempReverse = P_Child_ParentLeft_ParentRight * SingleCond_ParentLeft_ParentRight.GetValue(True, True) * Marginal_ParentRight.GetValue(True) / (SingleCond_Child_ParentLeft.GetValue(True, True) * Marginal_ParentLeft.GetValue(True)) m_Sanity1Value = TempReverse m_Sanity1Message = "Invalid combination of " & Name & ", " & SingleCond_ParentLeft_ParentRight.Name & ", " & Marginal_ParentRight.Name & ", " & _ SingleCond_Child_ParentLeft.Name & ", " & Marginal_ParentLeft.Name & ": P(" & Marginal_ParentRight.Node & "" & Marginal_ParentLeft.Node & _ ", " & Marginal_Child.Node & ") = " & m_Sanity1Value m_Values(2) = (1  TempReverse) * SingleCond_Child_ParentLeft.GetValue(True, True) * Marginal_ParentLeft.GetValue(True) / (SingleCond_ParentLeft_ParentRight.GetValue(True, False) * Marginal_ParentRight.GetValue(False)) 'calculate TRUE FALSE TRUE TempReverse = P_Child_ParentLeft_ParentRight * SingleCond_ParentLeft_ParentRight.GetValue(True, True) / SingleCond_Child_ParentRight.GetValue(True, True) m_Sanity2Value = TempReverse m_Sanity1Message = "Invalid combination of " & Name & ", " & SingleCond_ParentLeft_ParentRight.Name & ", " & SingleCond_Child_ParentRight.Name & ": P(" & Marginal_ParentLeft.Node & "" & Marginal_Child.Node & ", " & Marginal_ParentRig ht.Node & ") = " & m_Sanity2Value m_Values(3) = (1  TempReverse) * SingleCond_Child_ParentRight.GetValue(True, True) / SingleCond_ParentLeft_ParentRight.GetValue(False, True) 'calculate TRUE FALSE FALSE TempReverse = m_Values(2) * SingleCond_ParentLeft_ParentRight.GetValue(True, False) / SingleCond_Child_ParentRight.GetValue(True, False) m_Values(4) = (1  TempReverse) * SingleCond_Child_ParentRight.GetValue(True, False) / SingleCond_ParentLeft_ParentRight.GetValue(False, False) m_Values(5) = 1  m_Values(1) m_Values(6) = 1  m_Values(2) m_Values(7) = 1  m_Values(3) m_Values(8) = 1  m_Values(4) 'switch off functional for the single conditioned probabilities that are part of this double conditioned prob SingleCond_Child_ParentLeft.SetFunctional (False) SingleCond_Child_ParentRight.SetFunctional (False) End Sub Public Function GetValue(ByRef ChildState As Boolean, ByRef ParentLeftState As Boolean, ByRef ParentRightState As Boolean) As Double If ChildState = True And ParentLeftState = True And ParentRightState = True Then GetValue = m_Values(1) ElseIf ChildState = True And ParentLeftState = True And ParentRightState = False Then GetValue = m_Values(2) ElseIf ChildState = True And ParentLeftState = False And ParentRightState = True Then GetValue = m_Values(3) ElseIf ChildState = True And ParentLeftState = False And ParentRightState = False Then GetValue = m_Values(4) ElseIf ChildState = False And ParentLeftState = True And ParentRightState = True Then GetValue = m_Values(5)
55  P a g e
ElseIf ChildState = False And ParentLeftState = True And ParentRightState = False Then GetValue = m_Values(6) ElseIf ChildState = False And ParentLeftState = False And ParentRightState = True Then GetValue = m_Values(7) Else GetValue = m_Values(8) End If End Function Public Function GetValueByStates(ByRef Nodes() As String, ByRef States() As Boolean) As Double Dim PositionChild, PositionParentLeft, PositionParentRight As Integer Dim Found As Boolean 'find the childnode Found = False For PositionChild = 1 To UBound(Nodes) If Nodes(PositionChild) = m_ChildNode Then Found = True Exit For End If Next If Found = False Then MsgBox ("Error finding childnode: childnode " & m_ChildNode & " is not found in list of nodes") GetValueByStates = 1 Exit Function End If 'find the parentnode left Found = False For PositionParentLeft = 1 To UBound(Nodes) If Nodes(PositionParentLeft) = m_ParentNodeLeft Then Found = True Exit For End If Next If Found = False Then MsgBox ("Error finding parentnode left: parentnode left " & m_ParentNodeLeft & " is not found in list of nodes") GetValueByStates = 1 Exit Function End If 'find the parentnode right Found = False For PositionParentRight = 1 To UBound(Nodes) If Nodes(PositionParentRight) = m_ParentNodeRight Then Found = True Exit For End If Next If Found = False Then MsgBox ("Error finding parentnode right: parentnode right " & m_ParentNodeRight & " is not found in list of nodes") GetValueByStates = 1 Exit Function End If 'retrieve the correct value GetValueByStates = GetValue(States(PositionChild), States(PositionParentLeft), States(PositionParentRight)) End Function Public Function CheckSanity(ByRef Message As String) As Boolean CheckSanity = True If m_Values(1) < 0 Or m_Values(1) >= 1 Then Message = Message & vbCrLf & "Invalid value for " & Name & ": " & m_Values(1) CheckSanity = False End If If m_Sanity1Value < 0 Or m_Sanity1Value >= 1 Then
56  P a g e
Message = Message & vbCrLf & m_Sanity1Message CheckSanity = False End If If m_Sanity2Value < 0 Or m_Sanity2Value >= 1 Then Message = Message & vbCrLf & m_Sanity2Message CheckSanity = False End If End Function
57  P a g e