1/*
2 * Legal Notice
3 *
4 * This document and associated source code (the "Work") is a part of a
5 * benchmark specification maintained by the TPC.
6 *
7 * The TPC reserves all right, title, and interest to the Work as provided
8 * under U.S. and international laws, including without limitation all patent
9 * and trademark rights therein.
10 *
11 * No Warranty
12 *
13 * 1.1 TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, THE INFORMATION
14 * CONTAINED HEREIN IS PROVIDED "AS IS" AND WITH ALL FAULTS, AND THE
15 * AUTHORS AND DEVELOPERS OF THE WORK HEREBY DISCLAIM ALL OTHER
16 * WARRANTIES AND CONDITIONS, EITHER EXPRESS, IMPLIED OR STATUTORY,
17 * INCLUDING, BUT NOT LIMITED TO, ANY (IF ANY) IMPLIED WARRANTIES,
18 * DUTIES OR CONDITIONS OF MERCHANTABILITY, OF FITNESS FOR A PARTICULAR
19 * PURPOSE, OF ACCURACY OR COMPLETENESS OF RESPONSES, OF RESULTS, OF
20 * WORKMANLIKE EFFORT, OF LACK OF VIRUSES, AND OF LACK OF NEGLIGENCE.
21 * ALSO, THERE IS NO WARRANTY OR CONDITION OF TITLE, QUIET ENJOYMENT,
22 * QUIET POSSESSION, CORRESPONDENCE TO DESCRIPTION OR NON-INFRINGEMENT
23 * WITH REGARD TO THE WORK.
24 * 1.2 IN NO EVENT WILL ANY AUTHOR OR DEVELOPER OF THE WORK BE LIABLE TO
25 * ANY OTHER PARTY FOR ANY DAMAGES, INCLUDING BUT NOT LIMITED TO THE
26 * COST OF PROCURING SUBSTITUTE GOODS OR SERVICES, LOST PROFITS, LOSS
27 * OF USE, LOSS OF DATA, OR ANY INCIDENTAL, CONSEQUENTIAL, DIRECT,
28 * INDIRECT, OR SPECIAL DAMAGES WHETHER UNDER CONTRACT, TORT, WARRANTY,
29 * OR OTHERWISE, ARISING IN ANY WAY OUT OF THIS OR ANY OTHER AGREEMENT
30 * RELATING TO THE WORK, WHETHER OR NOT SUCH AUTHOR OR DEVELOPER HAD
31 * ADVANCE NOTICE OF THE POSSIBILITY OF SUCH DAMAGES.
32 *
33 * Contributors
34 * - Sergey Vasilevskiy
35 * - Doug Johnson
36 */
37
38/*
39 * Class representing the Holdings, Trades, Trade Request, Settlement, Trade
40 * History, and Cash Transaction tables.
41 */
42#ifndef HOLDINGS_AND_TRADES_TABLE_H
43#define HOLDINGS_AND_TRADES_TABLE_H
44
45#include "EGenTables_common.h"
46#include "CustomerAccountsAndPermissionsTable.h"
47#include "SecurityPriceRange.h"
48
49#include "input/DataFileManager.h"
50
51namespace TPCE {
52
53// Arrays for min and max bounds on the security ranges for different tier
54// accounts The indices into these arrays are
55// 1) the customer tier (zero based)
56// 2) the number of accounts for the customer (zero based)
57// Entries with 0 mean there cannot be that many accounts for a customer with
58// that tier.
59//
60const int iMinSecuritiesPerAccountRange[3][10] = {
61 {6, 4, 2, 2, 0, 0, 0, 0, 0, 0}, {0, 7, 5, 4, 3, 2, 2, 2, 0, 0}, {0, 0, 0, 0, 4, 4, 3, 3, 2, 2}};
62const int iMaxSecuritiesPerAccountRange[3][10] = {{14, 16, 18, 18, 00, 00, 00, 00, 00, 00},
63 {00, 13, 15, 16, 17, 18, 18, 18, 00, 00},
64 {00, 00, 00, 00, 16, 16, 17, 17, 18, 18}};
65const int iMaxSecuritiesPerAccount = 18; // maximum number of securities in a customer account
66
67// const double fMinSecPrice = 20;
68// const double fMaxSecPrice = 30;
69
70// These are used for picking the transaction type at load time.
71// NOTE that the corresponding "if" tests must be in the same order!
72const int cMarketBuyLoadThreshold = 30; // 1% - 30%
73const int cMarketSellLoadThreshold = cMarketBuyLoadThreshold + 30; // 31% - 60%
74const int cLimitBuyLoadThreshold = cMarketSellLoadThreshold + 20; // 61% - 80%
75const int cLimitSellLoadThreshold = cLimitBuyLoadThreshold + 10; // 81% - 90%
76const int cStopLossLoadThreshold = cLimitSellLoadThreshold + 10; // 91% - 100%
77
78const int iPercentBuysOnMargin = 16;
79
80// These are used when loading the table, and when generating runtime data.
81const int cNUM_TRADE_QTY_SIZES = 4;
82const int cTRADE_QTY_SIZES[cNUM_TRADE_QTY_SIZES] = {100, 200, 400, 800};
83
84// Percentage of trades modifying holdings in Last-In-First-Out order.
85//
86const int iPercentTradeIsLIFO = 35;
87
88// Number of RNG calls for one simulated trade
89const int iRNGSkipOneTrade = 11; // average count for v3.5: 6.5
90
91class CHoldingsAndTradesTable {
92 CRandom m_rnd;
93 CCustomerAccountsAndPermissionsTable m_CustomerAccountTable;
94
95 TIdent m_iSecCount; // number of securities
96 UINT m_iMaxSecuritiesPerCA; // number of securities per account
97 TIdent m_SecurityIds[iMaxSecuritiesPerAccount];
98 bool m_bCacheEnabled;
99 TIdent m_iCacheOffsetNS;
100 int m_iCacheSizeNS;
101 int *m_CacheNS;
102 TIdent m_iCacheOffsetSFFI;
103 int m_iCacheSizeSFFI;
104 TIdent *m_CacheSFFI;
105
106public:
107 // Constructor.
108 CHoldingsAndTradesTable(const DataFileManager &dfm,
109 UINT iLoadUnitSize, // # of customers in one load unit
110 TIdent iCustomerCount, TIdent iStartFromCustomer = iDefaultStartFromCustomer,
111 bool bCacheEnabled = false)
112 : m_rnd(RNGSeedTableDefault),
113 m_CustomerAccountTable(dfm, iLoadUnitSize, iCustomerCount, iStartFromCustomer, bCacheEnabled),
114 m_bCacheEnabled(bCacheEnabled) {
115 m_iSecCount = dfm.SecurityFile().GetConfiguredSecurityCount();
116
117 // Set the max number of holdings per account to be
118 // iMaxSecuritiesPerAccount
119 //
120 m_iMaxSecuritiesPerCA = iMaxSecuritiesPerAccount;
121
122 if (m_bCacheEnabled) {
123 m_iCacheSizeNS = iDefaultLoadUnitSize * iMaxAccountsPerCust;
124 m_iCacheOffsetNS =
125 m_CustomerAccountTable.GetStartingCA_ID(iStartFromCustomer) + (iTIdentShift * iMaxAccountsPerCust);
126 m_CacheNS = new int[m_iCacheSizeNS];
127 for (int i = 0; i < m_iCacheSizeNS; i++) {
128 m_CacheNS[i] = 0;
129 }
130
131 m_iCacheSizeSFFI = iDefaultLoadUnitSize * iMaxAccountsPerCust * iMaxSecuritiesPerAccount;
132 m_iCacheOffsetSFFI =
133 m_CustomerAccountTable.GetStartingCA_ID(iStartFromCustomer) + (iTIdentShift * iMaxAccountsPerCust);
134 m_CacheSFFI = new TIdent[m_iCacheSizeSFFI];
135 for (int i = 0; i < m_iCacheSizeSFFI; i++) {
136 m_CacheSFFI[i] = -1;
137 }
138 }
139 };
140
141 // Destructor
142 ~CHoldingsAndTradesTable() {
143 if (m_bCacheEnabled) {
144 delete[] m_CacheNS;
145 delete[] m_CacheSFFI;
146 }
147 };
148
149 /*
150 * Reset the state for the next load unit.
151 * Called only from the loader (CTradeGen), not the driver.
152 */
153 void InitNextLoadUnit(INT64 TradesToSkip, TIdent iStartingAccountID) {
154 m_rnd.SetSeed(m_rnd.RndNthElement(RNGSeedTableDefault,
155 // there is only 1 call to this RNG per trade
156 (RNGSEED)TradesToSkip));
157 if (m_bCacheEnabled) {
158 m_iCacheOffsetNS = iStartingAccountID;
159 for (int i = 0; i < m_iCacheSizeNS; i++) {
160 m_CacheNS[i] = 0;
161 }
162
163 m_iCacheOffsetSFFI = iStartingAccountID;
164 for (int i = 0; i < m_iCacheSizeSFFI; i++) {
165 m_CacheSFFI[i] = -1;
166 }
167 }
168
169 m_CustomerAccountTable.InitNextLoadUnit();
170 }
171
172 /*
173 * Generate the number of securities for a given customer account.
174 */
175 int GetNumberOfSecurities(TIdent iCA_ID, eCustomerTier iTier, int iAccountCount) {
176 int iNumberOfSecurities = 0;
177
178 // We will sometimes get CA_ID values that are outside the current
179 // load unit (cached range). We need to check for this case
180 // and avoid the lookup (as we will segfault or get bogus data.)
181 TIdent index = iCA_ID - m_iCacheOffsetNS;
182 bool bCheckCache = (index >= 0 && index < m_iCacheSizeNS);
183 if (m_bCacheEnabled && bCheckCache) {
184 iNumberOfSecurities = m_CacheNS[index];
185 }
186
187 if (iNumberOfSecurities == 0) {
188 RNGSEED OldSeed;
189 int iMinRange, iMaxRange;
190
191 iMinRange = iMinSecuritiesPerAccountRange[iTier - eCustomerTierOne][iAccountCount - 1];
192 iMaxRange = iMaxSecuritiesPerAccountRange[iTier - eCustomerTierOne][iAccountCount - 1];
193
194 OldSeed = m_rnd.GetSeed();
195 m_rnd.SetSeed(m_rnd.RndNthElement(RNGSeedBaseNumberOfSecurities, (RNGSEED)iCA_ID));
196 iNumberOfSecurities = m_rnd.RndIntRange(iMinRange, iMaxRange);
197 m_rnd.SetSeed(OldSeed);
198
199 if (m_bCacheEnabled && bCheckCache) {
200 m_CacheNS[index] = iNumberOfSecurities;
201 }
202 }
203 return iNumberOfSecurities;
204 }
205
206 /*
207 * Get seed for the starting security ID seed for a given customer id.
208 */
209 RNGSEED GetStartingSecIDSeed(TIdent iCA_ID) {
210 return (m_rnd.RndNthElement(RNGSeedBaseStartingSecurityID, (RNGSEED)iCA_ID * m_iMaxSecuritiesPerCA));
211 }
212
213 /*
214 * Convert security index within an account (1-18) into
215 * corresponding security index within the
216 * Security.txt input file (0-6849).
217 *
218 * Needed to be able to get the security symbol
219 * and other information from the input file.
220 *
221 * RETURNS:
222 * security index within the input file (0-based)
223 */
224 TIdent GetSecurityFlatFileIndex(TIdent iCustomerAccount, UINT iSecurityAccountIndex) {
225 TIdent iSecurityFlatFileIndex = -1;
226
227 // We will sometimes get CA_ID values that are outside the current
228 // load unit (cached range). We need to check for this case
229 // and avoid the lookup (as we will segfault or get bogus data.)
230 TIdent index = (iCustomerAccount - m_iCacheOffsetSFFI) * iMaxSecuritiesPerAccount + iSecurityAccountIndex - 1;
231 bool bCheckCache = (index >= 0 && index < m_iCacheSizeSFFI);
232 if (m_bCacheEnabled && bCheckCache) {
233 iSecurityFlatFileIndex = m_CacheSFFI[index];
234 }
235
236 if (iSecurityFlatFileIndex == -1) {
237 RNGSEED OldSeed;
238 UINT iGeneratedIndexCount = 0; // number of currently generated unique flat file indexes
239 UINT i;
240
241 OldSeed = m_rnd.GetSeed();
242 m_rnd.SetSeed(GetStartingSecIDSeed(iCustomerAccount));
243
244 iGeneratedIndexCount = 0;
245
246 while (iGeneratedIndexCount < iSecurityAccountIndex) {
247 iSecurityFlatFileIndex = m_rnd.RndInt64Range(0, m_iSecCount - 1);
248
249 for (i = 0; i < iGeneratedIndexCount; ++i) {
250 if (m_SecurityIds[i] == iSecurityFlatFileIndex)
251 break;
252 }
253
254 // If a duplicate is found, overwrite it in the same location
255 // so basically no changes are made.
256 //
257 m_SecurityIds[i] = iSecurityFlatFileIndex;
258
259 // If no duplicate is found, increment the count of unique ids
260 //
261 if (i == iGeneratedIndexCount) {
262 ++iGeneratedIndexCount;
263 }
264 }
265
266 m_rnd.SetSeed(OldSeed);
267
268 if (m_bCacheEnabled && bCheckCache) {
269 m_CacheSFFI[index] = iSecurityFlatFileIndex;
270 }
271 }
272 return iSecurityFlatFileIndex;
273 }
274
275 /*
276 * Generate random customer account and security to perfrom a trade on.
277 * This function is used by both the runtime driver (CCETxnInputGenerator)
278 * and by the loader when generating initial trades (CTradeGen).
279 *
280 */
281 void GenerateRandomAccountSecurity(TIdent iCustomer, // in
282 eCustomerTier iTier, // in
283 TIdent *piCustomerAccount, // out
284 TIdent *piSecurityFlatFileIndex, // out
285 UINT *piSecurityAccountIndex) // out
286 {
287 TIdent iCustomerAccount;
288 int iAccountCount;
289 int iTotalAccountSecurities;
290 UINT iSecurityAccountIndex; // index of the selected security in the
291 // account's basket
292 TIdent iSecurityFlatFileIndex; // index of the selected security in the
293 // input flat file
294
295 // Select random account for the customer
296 //
297 m_CustomerAccountTable.GenerateRandomAccountId(m_rnd, iCustomer, iTier, &iCustomerAccount, &iAccountCount);
298
299 iTotalAccountSecurities = GetNumberOfSecurities(iCustomerAccount, iTier, iAccountCount);
300
301 // Select random security in the account
302 //
303 iSecurityAccountIndex = (UINT)m_rnd.RndIntRange(1, iTotalAccountSecurities);
304
305 iSecurityFlatFileIndex = GetSecurityFlatFileIndex(iCustomerAccount, iSecurityAccountIndex);
306
307 // Return data
308 //
309 *piCustomerAccount = iCustomerAccount;
310 *piSecurityFlatFileIndex = iSecurityFlatFileIndex;
311 if (piSecurityAccountIndex != NULL) {
312 *piSecurityAccountIndex = iSecurityAccountIndex;
313 }
314 }
315
316 bool IsAbortedTrade(TIdent TradeId) {
317 bool bResult = false;
318 if (iAbortedTradeModFactor == TradeId % iAbortTrade) {
319 bResult = true;
320 }
321 return bResult;
322 }
323};
324
325} // namespace TPCE
326
327#endif // HOLDINGS_AND_TRADES_TABLE_H
328