Financial Applications using Excel Add-in Development in C/C++ phần 9 pps

59 158 0
Financial Applications using Excel Add-in Development in C/C++ phần 9 pps

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

446 Excel Add-in Development in C/C++ xloper * __stdcall ExampleActiveOnly(xloper *pTrigger) { static count = 0; // could be incremented by more than one thread cpp_xloper Op; Op.Excel(xlfCaller); // Set Op = caller's reference if(Op.IsActiveRef()) Op = ++count; // re-use Op for the return value else // return the last value Op.ConvertRefToValues(); // fails if not registered as type # return Op.ExtractXloper(); } The cpp_xloper’s member function Excel() calls Excel4v()/Excel12v(),and sets a flag to tell the class to use xlFree to free the memory. (xloper/xloper12s of type xltypeRef point to allocated memory). The code for IsActiveRef(), listed below, uses the C API-only function xlSheetId to obtain the ID of the active sheet. Note that this ID is not thesameasthe.Index property from the above VBA example, which is simply the index in the workbook’s collection of sheets. // Is the xloper a reference on the active sheet? bool cpp_xloper::IsActiveRef(void) const { DWORD id; if(gExcelVersion12plus) { if(m_Op12.xltype == xltypeSRef) // then convert to xltypeRef { xloper12 as_ref = {0, xltypeNil}; xloper12 type = {0, xltypeInt}; type.val.w = xltypeRef; Excel12(xlCoerce, &as_ref, 2, &m_Op12, &type); if(as_ref.xltype != xltypeRef) return false; id = as_ref.val.mref.idSheet; Excel12(xlFree, 0, 1, &as_ref); } else if(m_Op12.xltype == xltypeRef) id = m_Op12.val.mref.idSheet; else return false; xloper12 active_sheet_id; if(Excel12(xlSheetId, &active_sheet_id, 0) || active_sheet_id.xltype != xltypeRef || id != active_sheet_id.val.mref.idSheet) { // No need to call xlFree: active_sheet_id' s xlmref pointer is NULL return false; } } else { if(m_Op.xltype == xltypeSRef) // then convert to xltypeRef Miscellaneous Topics 447 { xloper as_ref = {0, xltypeNil}; xloper type = {0, xltypeInt}; type.val.w = xltypeRef; Excel4(xlCoerce, &as_ref, 2, &m_Op, &type); if(as_ref.xltype != xltypeRef) return false; id = as_ref.val.mref.idSheet; Excel4(xlFree, 0, 1, &as_ref); } else if(m_Op.xltype == xltypeRef) id = m_Op.val.mref.idSheet; else return false; xloper active_sheet_id; if(Excel4(xlSheetId, &active_sheet_id, 0) || active_sheet_id.xltype != xltypeRef || id != active_sheet_id.val.mref.idSheet) { // No need to call xlFree: active_sheet_id' s xlmref pointer is NULL return false; } } return true; } Excel 2007 multi-threading note: Excel 2007 regards functions registered as macro-sheet equivalents, type #, as thread-unsafe. This prevents ExampleActiveOnly() being registered as type $ in Excel 2007. You may also like to create worksheet functions that are only recalculated, say, when a button on the active sheet is pressed. One way to achieve this is to create functions that take an argument, perhaps optional, where the functions only recalculate when that argument is TRUE, and otherwise return the cell’s last value. Again, using VBA this is not possible. Using the C API this is straightforward, as the following function demonstrates. xloper * __stdcall ExampleRecalcSwitch(xloper *pArg, xloper *pDontRecalc) { cpp_xloper Op, DontRecalc(pDontRecalc); if(DontRecalc.IsTrue()) // then return the last value { Op.SetToCallerValue(); } else // recalculate { Op = pArg; Op = process_arg((double)Op); } return Op.ExtractXloper(); } 448 Excel Add-in Development in C/C++ bool cpp_xloper::SetToCallerValue(void) { Free(); if(gExcelVersion11minus) { // Get a reference to the calling cell(s) xloper caller; if(Excel4(xlfCaller, &caller, 0) != xlretSuccess) return false; if(!(caller.xltype & (xltypeRef | xltypeSRef))) { Excel4(xlFree, 0, 1, &caller); return false; } // Get the calling cell's value if(Excel4(xlCoerce, &m_Op, 1, &caller) != xlretSuccess) { Excel4(xlFree, 0, 1, &caller); return false; } return m_XLtoFree = true; } else { // Get a reference to the calling cell(s) xloper12 caller; if(Excel12(xlfCaller, &caller, 0) != xlretSuccess) return false; if(!(caller.xltype & (xltypeRef | xltypeSRef))) { Excel12(xlFree, 0, 1, &caller); return false; } // Get the calling cell's value if(Excel12(xlCoerce, &m_Op12, 1, &caller) != xlretSuccess) { Excel12(xlFree, 0, 1, &caller); return false; } return m_XLtoFree12 = true; } } Without using the cpp_xloper class, the above code could be implemented as follows in a function registered as type #. xloper * __stdcall ExampleRecalcSwitch(xloper *pArg, xloper *pDontRecalc) { // Not thread-safe, but this function must be registered as type # // so cannot also be registered as thread-safe in Excel 12 static xloper ret_val; if(pDontRecalc->xltype == xltypeBool && pDontRecalc->val.xbool == 1) { Miscellaneous Topics 449 xloper caller; Excel4(xlfCaller, &caller, 0); Excel4(xlCoerce, &ret_val, 1, &caller); Excel4(xlFree, 0, 1, &caller); ret_val.xltype |= xlbitXLFree; return &ret_val; } // else recalculate double result = pArg->xltype == xltypeNum ? pArg->val.num : 0.0; result = process_arg(result); ret_val.xltype = xltypeNum; ret_val.val.num = result; return &ret_val; } All that is then required is a control button on the workbook, a named cell to contain the switch, call it RecalcSwitch, and the following VBA event trap: Private Sub CommandButton1_Click() With Range("RecalcSwitch") .Value = True // Excel will recalc if calculation set to Automatic .Value = False End With End Sub One drawback with this approach is the fact that the functions that depend on RecalcSwitch are recalculated twice every time the button is pressed. In one of these cases recalculation is slow, and in the other fast, so this is not a serious concern for those functions themselves. However their dependents are also recalculated, so you should only do this where the dependents are few or fast and where the initial function execution time is very slow. 10 Example Add-ins and Financial Applications Developers are always faced with the need to balance freedoms and constraints when deciding the best way to implement a model. Arguably the most important skill a developer can have is the ability to choose the most appropriate approach all things considered: Failure can result in code that is cumbersome, or slow, or difficult to maintain or extend, or bug-ridden, or that fails completely to meet a completion time target. This chapter aims to do two things: 1. Present a few simple worksheet function examples that demonstrate some of the basic considerations, such as argument and return types. For these examples source code is included on the CD ROM in the example project. Sections 10.1 to 10.4 cover these functions. 2. Discuss the development choices available and constraints for a number of financial markets applications. Some of these applications are not all fully worked through in the book, and some source code is not provided on the CD ROM . Sections 10.5 and beyond cover these functions and applications. Some of the simple example functions could easily be coded in VBA or duplicated with perhaps only a small number of worksheet cells. The point is not to say that these things can only be done in C/C++ or using the C API. If you have decided that you want or need to use C/C++, these examples aim to provide a template or guide. The most important thing that an add-in developer must get right is the function inter- face. The choices made as to the types of arguments a function takes, are they required or optional; if optional what the default behaviour is; and so on, are often critical. Much of the discussion in this chapter is on this and similar issues, rather than on one algorithm versus another. The discussion of which algorithm to use, etc., is left to other texts and to the reader whose own experience may very well be more informed or advanced than the author’s. Important note: You should not rely on any of these examples, or the methods they contain, in your own applications without having completely satisfied yourself that they are correct and appropriate for your needs. They are intended only to illustrate how techniques discussed in earlier chapters can be applied. 10.1 STRING FUNCTIONS Excel has a number of very efficient basic string functions, but string operations can quickly become unnecessarily complex when just using these. Consider, for example, the case where you want to substitute commas for stops (periods) dynamically. This is easily done using Excel’s SUBSTITUTE(). However, if you want to simultaneously substitute commas for stops and stops for commas things are more complex. (You could do this in 452 Excel Add-in Development in C/C++ three applications of SUBSTITUTE(), but this is messy.) Writing a function in C that does this is straightforward (see replace_mask() below). The C and C++ libraries both contain a number of low-level string functions that can easily be given Excel worksheet wrappers. This section presents a number of example functions, some of which just wrap standard library functions. The code for all of these functions is listed in the Example project on the CD ROM in the source file XllStrings.cpp. When registered with Excel, they are added to the Text category. Excel 2007 gives the C API access to Unicode strings of much greater length than the byte-strings of earlier versions. Section 8.6.12 Registering functions with dual interfaces for Excel 2007 and earlier versions on page 263 explains how to register worksheet functions that call a different underlying DLL export depending on the running version. This enables your functions to get the optimum behaviour. The examples in this section are, therefore, given in both 2003− and 2007+ flavours. Function name count_char_xl4 or count_char_xl12 (exported) CountChar (registered with Excel) Description Counts the number of occurrences of a given ASCII character. Type string "HCP" (2003), "HC%Q$" (2007) Notes Function does not need to be volatile and does not access any C API functions that might require it to be registered as a macro sheet equivalent function. 2007 version is thread-safe. // Core functions size_t count_char(char *text, char ch) { if(!text ||!ch) return 0; for(size_t count = 0; *text; ) if(*text++ == ch) count++; return count; } size_t count_char(wchar_t *text, wchar_t ch) { if(!text ||!ch) return 0; for(size_t count = 0; *text; ) if(*text++ == ch) count++; return count; } // Excel 11- interface function. Uses xlopers and byte-string size_t __stdcall count_char_xl4(char *text, xloper *p_ch) { cpp_xloper Ch(p_ch); char ch; if(Ch.IsStr()) Example Add-ins and Financial Applications 453 ch = (char)Ch.First(); else if(Ch.IsNum()) ch = (char)(double)Ch; else return 0; return count_char(text, ch); } // Excel 12+ interface function. Uses xloper12s and Unicode string size_t __stdcall count_char_xl12(wchar_t *text, xloper12 *p_ch) { cpp_xloper Ch(p_ch); wchar_t ch; if(Ch.IsStr()) ch = Ch.First(); else if(Ch.IsNum()) ch = (wchar_t)(double)Ch; else return 0; return count_char(text, ch); } Function name replace_mask_xl4 or replace_mask_xl12 (exported) ReplaceMask (registered with Excel) Description Replaces all occurrences of characters in a search string with corresponding characters from a replacement string, or removes all such occurrences if no replacement string is provided. Type string "1FCP" (2003), "1F%C%Q$" (2007) Notes Declared as returning void. Return value is the 1st argument modified in place. Third argument is optional and passed as a value xloper/xloper12 (see section 6.2.6) to avoid the need to dereference a range reference. // Core functions void replace_mask(char *text, char *old_chars, char *new_chars) { if(!text ||!old_chars) return; char *p_old, *p, *pt; if(!new_chars) { // Remove all occurrences of all characters in old_chars for(p_old = old_chars; *p_old; p_old++) { 454 Excel Add-in Development in C/C++ for(pt = text; *pt;) { if(*pt == *p_old) { p = pt; do {*p = p[1];} while (*(++p)); } else pt++; } } return; } // Substitute all occurrences of old chars with corresponding new if(strlen(old_chars) != strlen(new_chars)) return; char *p_new; for(p = text; *p; p++) { p_old = old_chars; p_new = new_chars; for(; *p_old; p_old++, p_new++) { if(*p == *p_old) { *p = *p_new; break; } } } } void replace_mask(wchar_t *text, wchar_t *old_chars, wchar_t *new_chars); // Excel 11- interface function. Uses xlopers and byte-string void __stdcall replace_mask_xl4(char *text, char *old_chars, xloper *p_new_chars) { cpp_xloper NewChars(p_new_chars); char *new_chars = NewChars.IsStr() ? (char *)NewChars : NULL; if(new_chars) { replace_mask(text, old_chars, new_chars); free(new_chars); } } // Excel 12+ interface function. Uses xloper12s and Unicode string void __stdcall replace_mask_xl12(wchar_t *text, wchar_t *old_chars, xloper12 *p_new_chars) { cpp_xloper NewChars(p_new_chars); wchar_t *new_chars = NewChars.IsStr() ? (wchar_t *)NewChars : NULL; if(new_chars) { Example Add-ins and Financial Applications 455 replace_mask(text, old_chars, new_chars); free(new_chars); } } Function name reverse_text_xl4 or reverse_text_xl12 (exported) Reverse Text (registered with Excel) Description Reverses a string. Prototype void __stdcall reverse_text(char *text); Type string "1F" (2003), "1F%$" (2007) Notes Declared as returning void. Return value is the 1st argument modified in place. These functions simply wrap the C library functions strrev() and wcsrev(), and are useful in the creation of Halton quasi-random number sequences, for example. // Excel 11- interface function. Uses xlopers and byte-string void __stdcall reverse_text_xl4(char *text) {strrev(text);} // Excel 12+ interface function. Uses xloper12s and Unicode string void __stdcall reverse_text_xl12(wchar_t *text) {wcsrev(text);} Function name find_first_xl4 or find_first_xl12 (exported) FindFirst (registered with Excel) Description Returns the position of the first occurrence of any character from a search string, or zero if none found. Type string "HCC" (2003), "HC%C%$" (2007) Notes Any error in input is reflected with a zero return value, rather than an error type. These functions simply wrap the C library functions strpbrk() and wcspbrk(). // Core functions size_t find_first(char *text, char *search_text) { if(!text ||!search_text) return 0; char *p = strpbrk(text, search_text); return p ? 1 + p - text : 0; } size_t find_first(wchar_t *text, wchar_t *search_text) { [...]... // Core #define #define #define function NDINV_ITER_LIMIT NDINV_EPSILON NDINV_FIRST_NUDGE 473 50 1e-12 // How precise do we want to be 1e-7 // Minimum change in answer from one iteration to the next #define NDINV_DELTA 1e-10 // Approximate working limits of Excel' s NORMSINV() function in Excel 2000 #define NORMSINV_LOWER_LIMIT 3.024e-7 #define NORMSINV_UPPER_LIMIT 0 .99 999 9 void ndist_inv(double prob,... types of interpolation, this kind of pre-processing might make a significant enough difference to warrant it being split into two tasks (See splines in the next section) Function name interp_xl4 or interp_xl12 (exported) Interp (registered with Excel) Description Takes two columns of inputs, the first being values of x in ascending order, the second being corresponding values of y 478 Excel Add -in Development. .. underlying variable (and its inverse) Excel provides four built -in functions: NORMDIST(), NORMSDIST(), NORMINV() and NORMSINV() In version 9 (Excel 2000) and earlier there are a number of serious problems with the working ranges and accuracy of these functions: • The inverse functions are not precise inverses; • The range of probabilities for which NORMSINV() works is roughly 3.024e-7 to 0 .99 999 9; •... determines how far from the first of the two bracketing points x is int col_low_index, col_high_index; double col_t; Example Add-ins and Financial Applications 481 if(!bilinear_calc_t(num_cols, col_x_vals, col_x, col_low_index, col_high_index, col_t)) return false; // Extract the four y-values corresponding to the corners of the // rectangle described by row_low_index, row_high_index, // col_low_index,... reasonable approximation that is more accurate in the tails than the Taylor series implemented above It also appears to be the basis of Excel s own functions for versions 2000 and earlier #define #define #define #define #define #define #define B1 0.3 193 8153 B2 -0.356563782 B3 1.78147 793 7 B4 -1.82125 597 8 B5 1.3302744 29 PP 0.23164 19 NEG_LN_ROOT_2PI -0 .91 893 8533204673 double cndist(double d) { if(d ==... of interpolation: • Linear interpolation • Bilinear interpolation • Cubic spline ◦ Natural ◦ Gradient constrained at one end ◦ Gradient constrained at both ends The assumption is that there exists a table of known x’s and known y’s, sorted in ascending order of x, and that the user wishes to interpolate/extrapolate some unknown value of y for a given value of x 10.4.1 Linear interpolation Linear interpolation... persistent structure in the add -in, it is advisable to link its existence to the cell that created it, so that deletion of the cell’s contents, or of the cell itself, can be used as a trigger for freeing the resources used This also enables the return value for the sequence to be passed as a parameter to a worksheet function 470 Excel Add -in Development in C/C++ (See sections 9. 6 Maintaining large data...456 Excel Add -in Development in C/C++ if(!text | | !search_text) return 0; wchar_t *p = wcspbrk(text, search_text); return p ? 1 + p - text : 0; } // Excel 11- interface function Uses xlopers and byte-string size_t stdcall find_first_xl4(char *text, char *search_text) { return find_first(text, search_text); } // Excel 12+ interface function Uses xloper12s and Unicode string size_t stdcall find_first_xl12(wchar_t... Used in bilinear interpolation: y(x1, x2) = (1-t1)(1-t2).yll + t1(1-t2).yhl + t1.t2.yhh + (1-t1)t2.ylh bool bilinear_calc_t(int n_vals, double *x_vals, double x, int &low_index, int &high_index, double &t) { if(!get_bracket(n_vals, x_vals, x, low_index, high_index)) return false; if(low_index == -1) if(high_index == n_vals) low_index = 0; high_index ; if(x_vals[high_index] == x_vals[low_index]) // Could... BilinearInterp See also Bilinear Interpolation Example.xls ) Note that the following code will not extrapolate outside the bounds of the given x ranges Note also that the function bilinear_interp_xl4() simply sets up the call to bilinear_interp() This separates the Excel- specific input checking and data types from the core code It would be trivial to also create an Excel 2007 data type version of bilinear_interp_xl4(), . stops for commas things are more complex. (You could do this in 452 Excel Add -in Development in C/C++ three applications of SUBSTITUTE(), but this is messy.) Writing a function in C that does this. (char)(double)Ch; else return 0; return find_last(text, ch); } 458 Excel Add -in Development in C/C++ // Excel 12+ interface function. Uses xloper12s and Unicode string size_t __stdcall find_last_xl12(wchar_t *text,. Op.ExtractXloper(); } 448 Excel Add -in Development in C/C++ bool cpp_xloper::SetToCallerValue(void) { Free(); if(gExcelVersion11minus) { // Get a reference to the calling cell(s) xloper caller; if (Excel4 (xlfCaller,

Ngày đăng: 14/08/2014, 06:22

Từ khóa liên quan

Tài liệu cùng người dùng

  • Đang cập nhật ...

Tài liệu liên quan