Ticket #3356: sage-3356-part2.patch
File sage-3356-part2.patch, 13.0 KB (added by , 14 years ago) |
---|
-
sage/finance/markov_multifractal.py
# HG changeset patch # User William Stein <wstein@gmail.com> # Date 1212514292 25200 # Node ID 1f137599f3e3141f261529ab6930ae9880b2443d # Parent b76f5b58a6addd3c0a302b79d7103d90aed4a435 trac #3356 finance -- more work on quality random number generation; vastly optimized multifractal model diff -r b76f5b58a6ad -r 1f137599f3e3 sage/finance/markov_multifractal.py
a b class MarkovSwitchingMultifractal: 164 164 self.__gamma = gamma 165 165 return gamma 166 166 167 def simulation(self, T):167 def simulation(self, n): 168 168 """ 169 Retun a time series of the T values taken by simulating the 170 running of this Markov switching multifractal model for T time 171 steps. 169 Same as self.simulations, but run only 1 time, and returns a time series 170 instead of a list of time series. 172 171 173 172 INPUT: 174 T -- a positive integer 175 OUTPUT: 176 list -- a list of floats, the returns of the 177 log-prices of a financial asset or 178 exchange rate. 173 n -- a positive integer 179 174 180 175 EXAMPLES: 181 sage: cad_usd = finance.MarkovSwitchingMultifractal(10,1.278,0.262,0.644,2.11); cad_usd182 Markov switching multifractal model with m0 = 1.278, sigma = 0.262, b = 2.11, and gamma_10 = 0.644183 sage: v = cad_usd.simulation(100)184 sage: v # random -- using seed doesn't work; planned rewrite of this function will work185 [0.0011, -0.0032, 0.0006, 0.0007, 0.0034 ... -0.0023, 0.0008, 0.0015, -0.0003, 0.0027]186 sage: v.sums() # random187 [0.0011, -0.0021, -0.0015, -0.0008, 0.0026 ... -0.0383, -0.0376, -0.0360, -0.0363, -0.0336]188 sage: v.sums().exp().plot()189 176 """ 190 # Two values of the distribution M. 191 m0 = self.m0() 192 vals = [m0, 2 - m0] 177 return self.simulations(n, 1)[0] 193 178 194 # Initalize the Markov volatility state vector195 from sage.rings.all import RDF196 kbar = self.kbar()197 m = (RDF**kbar)([random.choice(vals) for _ in xrange(kbar)])198 199 sigma = self.sigma()/100.0200 201 # r is the time series of returns202 r = time_series.TimeSeries(T)203 204 # Generate T Gaussian random numbers with mean 0205 # and variance 1.206 import scipy.stats207 eps = scipy.stats.norm().rvs(T)208 209 # Generate uniform distribution around 0 between -1 and 1210 uniform = scipy.stats.uniform().rvs(kbar*T)211 212 # The gamma_k213 gamma = self.gamma()214 215 for t in range(T):216 r[t] = sigma * eps[t] * m.prod().sqrt()217 # Now update the volatility state vector218 j = t*kbar219 for k in range(kbar):220 if uniform[j+k] <= gamma[k]:221 # Draw from the distribution222 m[k] = random.choice(vals)223 224 return r225 226 179 def simulations(self, n, k=1): 227 180 """ 228 181 Return k simulations of length n using this Markov switching … … class MarkovSwitchingMultifractal: 230 183 231 184 INPUT: 232 185 n -- positive integer; number of steps 233 k -- positive integer ; number of simulations.186 k -- positive integer (default: 1); number of simulations. 234 187 235 188 OUTPUT: 236 189 list -- a list of TimeSeries objects. … … class MarkovSwitchingMultifractal: 241 194 """ 242 195 return markov_multifractal_cython.simulation(n, k, 243 196 self.__m0, self.__sigma, self.__b, 244 self.__ gamma_kbar, self.__kbar)197 self.__kbar, self.gamma()) 245 198 246 199 247 200 -
sage/finance/markov_multifractal_cython.pyx
diff -r b76f5b58a6ad -r 1f137599f3e3 sage/finance/markov_multifractal_cython.pyx
a b Cython code 4 4 Cython code 5 5 """ 6 6 7 cdef extern from "stdlib.h": 8 long random() 7 from sage.misc.randstate cimport randstate, current_randstate 8 9 cdef extern from "math.h": 10 double sqrt(double) 9 11 10 12 from time_series cimport TimeSeries 11 13 12 14 def simulation(Py_ssize_t n, Py_ssize_t k, 13 15 double m0, double sigma, double b, 14 double gamma_kbar, int kbar):16 int kbar, gamma): 15 17 cdef double m1 = 2 - m0 16 cdef Py_ssize_t i, j 17 cdef TimeSeries t 18 cdef Py_ssize_t i, j, a, c 19 cdef TimeSeries t, eps 18 20 cdef TimeSeries markov_state_vector = TimeSeries(kbar) 21 cdef TimeSeries gamma_vals = TimeSeries(gamma) 22 cdef randstate rstate = current_randstate() 19 23 20 24 sigma = sigma / 100.0 # model's sigma is a percent 21 25 … … def simulation(Py_ssize_t n, Py_ssize_t 25 29 for i from 0 <= i < k: 26 30 # Initalize the model 27 31 for j from 0 <= j < kbar: 28 markov_state_vector._values[j] = m0 if random()%2 else m132 markov_state_vector._values[j] = m0 if (rstate.c_random() & 1) else m1 # n & 1 means "is odd" 29 33 t = TimeSeries(n) 34 35 # Generate n normally distributed random numbers with mean 0 36 # and variance 1. 37 eps = TimeSeries(n) 38 eps.randomize('normal') 39 40 for a from 0 <= a < n: 41 # Compute next step in the simulation 42 t._values[a] = sigma * eps._values[a] * sqrt(markov_state_vector.prod()) 43 44 # Now update the volatility state vector 45 j = a * kbar 46 for c from 0 <= c < kbar: 47 if rstate.c_rand_double() <= gamma_vals._values[c]: 48 markov_state_vector._values[k] = m0 if (rstate.c_random() & 1) else m1 49 50 S.append(t) 51 52 return S 53 30 54 31 55 32 56 -
sage/finance/time_series.pyx
diff -r b76f5b58a6ad -r 1f137599f3e3 sage/finance/time_series.pyx
a b include "../ext/cdefs.pxi" 35 35 include "../ext/cdefs.pxi" 36 36 include "../ext/stdsage.pxi" 37 37 include "../ext/python_string.pxi" 38 include "../ext/random.pxi" 38 39 39 40 cdef extern from "math.h": 40 41 double exp(double) … … cdef extern from "string.h": 45 46 cdef extern from "string.h": 46 47 void* memcpy(void* dst, void* src, size_t len) 47 48 48 cdef extern from "stdlib.h":49 long random()50 long RAND_MAX51 52 cdef inline float ranf():53 """54 Return random number uniformly distributed in 0..1.55 """56 return (<float> random())/RAND_MAX57 58 49 from sage.rings.integer import Integer 59 50 from sage.rings.real_double import RDF 60 51 from sage.modules.real_double_vector cimport RealDoubleVectorSpaceElement … … cdef class TimeSeries: 916 907 for i from 0 <= i < self._length: 917 908 s += self._values[i] 918 909 return s 910 911 def prod(self): 912 """ 913 Return the prod of all the entries of self. If self has 914 length 0, returns 1. 915 916 OUTPUT: 917 double 918 919 EXAMPLES: 920 sage: v = finance.TimeSeries([1,1,1,2,3]); v 921 [1.0000, 1.0000, 1.0000, 2.0000, 3.0000] 922 sage: v.prod() 923 6.0 924 """ 925 cdef double s = 1 926 cdef Py_ssize_t i 927 for i from 0 <= i < self._length: 928 s *= self._values[i] 929 return s 930 919 931 920 932 def mean(self): 921 933 """ … … cdef class TimeSeries: 1051 1063 else: 1052 1064 return s 1053 1065 1066 def clip(self, min=None, max=None): 1067 """ 1068 Return new time series obtained from self by removing all values 1069 <= a certain maximum value, >= a certain minimum, or both. 1070 1071 INPUT: 1072 min -- None or double 1073 max -- None or double 1074 1075 OUTPUT: 1076 time seriessx 1077 """ 1078 cdef Py_ssize_t i, j 1079 cdef TimeSeries t 1080 cdef double mn, mx 1081 cdef double x 1082 if min is None and max is None: 1083 return self.copy() 1084 # This code is ugly but I think as fast as possible. 1085 if min is None and max is not None: 1086 # Return everything <= max 1087 j = 0 1088 mx = max 1089 for i from 0 <= i < self._length: 1090 if self._values[i] <= mx: 1091 j += 1 1092 1093 t = TimeSeries(j) 1094 j = 0 1095 for i from 0 <= i < self._length: 1096 if self._values[i] <= mx: 1097 t._values[j] = self._values[i] 1098 j += 1 1099 elif max is None and min is not None: 1100 # Return everything >= min 1101 j = 0 1102 mn = min 1103 for i from 0 <= i < self._length: 1104 if self._values[i] >= mn: 1105 j += 1 1106 t = TimeSeries(j) 1107 j = 0 1108 for i from 0 <= i < self._length: 1109 if self._values[i] >= mn: 1110 t._values[j] = self._values[i] 1111 j += 1 1112 else: 1113 # Return everything between min and max 1114 j = 0 1115 mn = min; mx = max 1116 for i from 0 <= i < self._length: 1117 x = self._values[i] 1118 if x >= mn and x <= mx: 1119 j += 1 1120 t = TimeSeries(j) 1121 j = 0 1122 for i from 0 <= i < self._length: 1123 x = self._values[i] 1124 if x >= mn and x <= mx: 1125 t._values[j] = x 1126 j += 1 1127 return t 1128 1054 1129 def histogram(self, Py_ssize_t bins=50, bint normalize=False): 1055 1130 """ 1056 1131 Return the frequency histogram of the values in … … cdef class TimeSeries: 1118 1193 1119 1194 INPUT: 1120 1195 bins -- positive integer (default: 50) 1121 normalize -- whether to normalize so the total area in the1122 bars of the histogram is 1.1196 normalize -- (default: True) whether to normalize so the total 1197 area in the bars of the histogram is 1. 1123 1198 **kwds -- passed to the bar_chart function 1124 1199 OUTPUT: 1125 1200 a histogram plot … … cdef class TimeSeries: 1153 1228 1154 1229 def randomize(self, distribution='uniform', loc=0, scale=1, **kwds): 1155 1230 """ 1231 Randomize the entries in this time series. 1232 1156 1233 INPUT: 1157 distribution -- 'uniform' 1158 'normal' 1159 'semicircle' 1234 distribution -- 'uniform': from loc to loc + scale 1235 'normal': mean loc and standard deviation scale 1236 'semicircle': with center at loc (scale is ignored) 1237 loc -- float (default: 0) 1238 scale -- float (default: 1) 1239 1240 NOTE: All random numbers are generated using the high quality 1241 GMP random number function gmp_urandomb_ui. This respects the 1242 Sage set_random_state command. It's not quite as fast at the 1243 C library random number generator, but is better quality, and 1244 is platform independent. 1160 1245 """ 1161 1246 if distribution == 'uniform': 1162 1247 self._randomize_uniform(loc, scale) … … cdef class TimeSeries: 1170 1255 def _randomize_uniform(self, double left, double right): 1171 1256 if left >= right: 1172 1257 raise ValueError, "left must be less than right" 1258 1259 cdef randstate rstate = current_randstate() 1173 1260 cdef Py_ssize_t k 1174 1261 cdef double d = right - left 1175 1262 for k from 0 <= k < self._length: 1176 self._values[k] = r anf() * d -left1263 self._values[k] = rstate.c_rand_double() * d + left 1177 1264 1178 1265 def _randomize_normal(self, double m, double s): 1179 1266 """ … … cdef class TimeSeries: 1185 1272 """ 1186 1273 # Ported from http://users.tkk.fi/~nbeijar/soft/terrain/source_o2/boxmuller.c 1187 1274 # This the box muller algorithm. 1275 cdef randstate rstate = current_randstate() 1276 1188 1277 cdef double x1, x2, w, y1, y2 1189 1278 cdef Py_ssize_t k 1190 1279 for k from 0 <= k < self._length: 1191 1280 while 1: 1192 x1 = 2.0 * r anf() - 1.01193 x2 = 2.0 * r anf() - 1.01281 x1 = 2.0 * rstate.c_rand_double() - 1.0 1282 x2 = 2.0 * rstate.c_rand_double() - 1.0 1194 1283 w = x1 * x1 + x2 * x2 1195 1284 if w < 1.0: break 1196 1285 w = sqrt( (-2.0 * log( w ) ) / w ) … … cdef class TimeSeries: 1204 1293 def _randomize_semicircle(self, double center): 1205 1294 cdef Py_ssize_t k 1206 1295 cdef double x, y, s, d = 2, left = center - 1, z 1296 cdef randstate rstate = current_randstate() 1207 1297 z = d*d 1208 1298 s = 1.5707963267948966192 # pi/2 1209 1299 for k from 0 <= k < self._length: 1210 1300 while 1: 1211 x = r anf() * d - 11212 y = r anf() * s1301 x = rstate.c_rand_double() * d - 1 1302 y = rstate.c_rand_double() * s 1213 1303 if y*y + x*x < 1: 1214 1304 break 1215 1305 self._values[k] = x + center