ServerPortfolio  2.0
Python parsers and server
 All Classes Namespaces Files Functions Variables Properties Pages
Stock.py
Go to the documentation of this file.
1 ## @package serverportfolio.Stock
2 #
3 # Define the classes Stock and InvalidStock
4 #
5 # Last Changed $Id: Stock.py 29 2015-05-05 11:09:54Z michael $
6 
7 # generator for interactive sessions ?
8 # http://www.jeffknupp.com/blog/2013/04/07/improve-your-python-yield-and-generators-explained/
9 #
10 
11 # namedtuple can avoid nested dictionary, from C++ ? for efficiency ?
12 # http://stackoverflow.com/questions/12119612/nicer-way-to-iterate-to-dictionary-in-python-to-avoid-many-nested-for-loops
13 
14 import sys, os, logging, types
15 import time, datetime, math
16 
17 from lxml import objectify, etree
18 
19 # for eclipse debugging with multithread, example in _add_inst_value
20 #import pydevd
21 
23 from serverportfolio import Utils
24 import serverportfolio.GlobalDicts as GlobalDicts
25 from serverportfolio.GlobalDicts import EAction
26 from serverportfolio.StockTemplates import StTmpl
27 # C++ extension, could check here or in ROOT (to put in PYTHONPATH also)
28 from serverportfolio import wrapPyStock
29 
30 # cannot import, recursive import
31 #from serverportfolio.DictionaryStocks import DictionaryStocks
32 #from serverportfolio.UpdateStocks import UpdateStocks
33 
34 from serverportfolio.ValidStockUpdate import ValidStockUpdate
35 
36 # to delete, shoudl be called only from ValidStockUpdate ?
37 from Validation import ValidationTkinter #, ValidationTkHP
38 
39 
40 ## @class Stock
41 # @brief Store data and functions related to one stock.
42 #
43 # Stock is a collection of dictionary templates copied from StockTemplates:
44 # - _dict_stock : Store data coming from the parsers (same action templates are used).
45 # Data are kept in memory(need for Static) except for HistPrice and DivSplit (delete long 'list_X').
46 # It contains also the 'Static' part (to make apart: Stock._dict_static (with get_static()), but like ?)
47 #
48 # - _dict_xml : used for reading/writing xml, extends _dict_stock (need (a list of) previous and new data to be inserted after validation.
49 # Data to keep or delete ? To load/keep after parsing for further queries.
50 #
51 # - _dict_interactive : intermediate dictionary to validate an update of the files, semi-automated or validated by user.
52 # 2 versions/templates needed: xml/csv. Validation and merging are different. 2 sets of functions also
53 # Only intermediate for the validation stage, could be in a Validation class. Actually filled, no template existing.
54 #
55 #
56 # Provides functions to read/save data:
57 # - HistoricalPrice / InstValue/ DivSplit are saved in CSV format with the use of C++ wrapper functions from wrapPyStock.so
58 # - Info / Fundamental / (DivSplit) are read and saved in XML format, only use python code.
59 #
60 # Functions for querying and updating data from the web are implemented in UpdateStocks
61 #
62 # Stock could have derived class (asset, FX, indice...), only done InvalidStock
63 class Stock(object):
64 
65  ## @brief Constructor.
66  # Creates the stock and save the 'Static' data read in the configuration/definition file "dictstocks.txt".\n
67  # Other (sub-)dictionaries will be created on demand: get_dict_parser(), get_dict_xml()\n
68  #
69  # If needed, one Stock can be constructed this way:\n
70  # @code stock = Stock( "CAC40", DictionaryStocks().get_dict_static("CAC40") ) @endcode
71  # But the use of DictionaryStocks is more convenient to store multiple Stock objects.
72  #
73  # @param symbol of the stock as defined in dictstocks.txt
74  # @param dict_static 'Static' template containing the data read from dictstocks.txt
75  def __init__(self, symbol, dict_static = None):
76  self._logger = logging.getLogger('SP.stock')
77  self._logger.debug("Init Stock %s" % symbol)
78 
79  ## @brief Main key for all dictionaries
80  self._symbol = symbol
81 
82  ## @brief Set of templates to store results from parsers, include 'Static' data
83  self._dict_stock=None
84  self._dict_stock={}
85  # dict_static has the correct format, not new_data from parser
86  if dict_static:
87  self.set_action('Static', None, dict_static)
88  #self._dict_stock['Static'] = dict_static
89  ## @brief Dictionary for storing data for XML input/output, extends _dict_parser templates.
90  # Initialised in read_xml()
91  self._dict_xml = None
92 
93 # almost all done in ValidStockUpdate, just problem with save_hist_price. Should use self.dict_stock, updated from ValidStockUpdate
94  ## @brief Store old/new values before update which may be applied to XML.\n
95  # In case of interactive mode, it can be validated by the user
96  # could be in separate class Validation() only intermediate data
97  self._dict_interactive = None
98  ## @brief Validation is done by a ValidStockUpdate object.
99  self.valid_update = None
100 
101 # ### Static method
102 
103  ## @brief Similar to Utils.to_list but deals with Stock or list of Stocks
104  # @param one stock symbol (String) or Stock object, or a list of them
105  # @return Always a list of stock symbols or stock objects
106  @staticmethod
107  def to_list(stock):
108  # already a list is input
109  if type(stock) == types.ListType :
110  return stock
111 
112  # if one element, make a list, extended to InvalidStock
113  if ( type(stock) == types.StringType) | ( isinstance(stock,Stock)) | ( isinstance(stock,InvalidStock)) :
114  #print "it is a string"
115  list_stock = []
116  list_stock.append( stock )
117  return list_stock
118 
119  # error, not tested ?
120  return None
121 
122  ## @brief Return if the Stock is valid.
123  # InvalidStock implements the same function, but return False
124  def is_valid(self):
125  return True
126 
127  ## @name Getter/Setter for internal dictionaries, provide initialisation on request
128  ## @{
129 
130  ## @brief Return the action template part from _dict_stock or one of its data value.
131  # If action is None, return the root dictionary _dict_stock\n
132  # If key is None return _dict_stock[action]['template']\n
133  # If _dict_stock[ action ] is not existing, create an 'action' entry with the default template and return a link\n
134  # If the key is not exiting, it is created (the action as well if needed), set it up to None and return it\n
135  #
136  #
137  # The purpose of this design is to provide lazy initialiation of the action / key, but some care must be taken when calling the function,\n
138  # get functions can modify the internal dictionary\n
139  #
140  # Example of usage:\n
141  # @code
142  # from DictionaryStocks import DictionaryStocks
143  # from Stock import Stock
144  #
145  # # Creates a Stock with 'Static' data
146  # stock = Stock('CAC40', DictionaryStocks().get_dict_static('CAC40'))
147  #
148  # # Extract the 'Static' template and one specific value, do not modify the dictionary
149  # templ_static = stock.get_action('Static')
150  # print templ_static # >>> {'code_bourso': '1rPCAC', 'code_yahoo': '^FCHI', 'market': 'europe', 'format_bourso': 'Pts'}
151  # code_yah = stock.get_action('Static','code_yahoo')
152  # print code_yah # >>> ^FCHI
153  #
154  # # Test a non-existing action, without modifying the internal dictionary
155  # if 'InstValue' in stock.get_action():
156  # print "InstValue is present"
157  # >>>
158  #
159  # # Only Static is present
160  # # Equivalent to stock._dict_stock
161  # stock.get_action() # >>> {'Static': {'config': {'save': False}, 'template': {'code_bourso': '1rPCAC', 'code_yahoo': '^FCHI',...}}}
162  #
163  # # Test a non-existing entry, action (and key) are created with default values the first time (default sate 'CLOSED')
164  # state = stock.get_action('InstValue','state')
165  # state # >>> CLOSED
166  # # InstValue action has been loaded
167  # print stock.get_action() # >>> {'InstValue': {'config': {'save': ['csv']}, 'template': {'variation': 0, ..}}, 'Static': {...}}
168  #
169  # # To not modify the dictionary, one needs 2 explicit loops
170  # if 'HistPrice' in stock.get_action():
171  # if 'list_csv' in stock.get_action('HistPrice'): # not executed
172  # list_csv='list_value'
173  # stock.get_action() # >>> No 'HistPrice' entry
174  # @endcode
175  #
176  # @note if the function return a link to a dictionary, if the link is modified the original dictionary is modified also.\n
177  # If the function return a value, a copy is returned.\n
178  # see unit_test.test_stock for more case
179  #
180  # @param action optional string corresponding to a GlobalDicts.EAction name
181  # @param key optional string to get the value of _templ_action['template'][key]
182  def get_action(self, action = None, key = None):
183  # create all on demand, can skip always dictionary
184  #if self._dict_parser == None:
185  # self._dict_parser = {}
186 
187  # return the full dictionary
188  if action == None:
189  return self._dict_stock
190 
191  if action in self._dict_stock:
192 
193  if key is None:
194  # to create or set to None by default ?
195  return self._dict_stock[action]['template']
196 
197  elif key in self._dict_stock[action]['template']:
198  return self._dict_stock[action]['template'][key]
199 
200  # create a new key, needed for 'source', other, or to declare in all templates
201  else:
202  self._dict_stock[action]['template'][key] = None
203  return self._dict_stock[action]['template'][key]
204 
205  # create an action entry with default template and config
206  # should be called only once during the lifetime of the object
207  e_action = Utils.stringToEAction( action )
208  self._dict_stock[ e_action.name ] = StTmpl.get_template_stock( e_action.name )
209 
210  # recursive call, seems working... need more tests
211  return self.get_action( action, key )
212 
213  ## @brief Fill _dict_stock['action'] with new_data.
214  # If already existing overwrite the data, otherwise create the necessary action
215  # @pre action != None, key can be None\n
216  # new_data must follow the format of a template _templ_X
217  # @post _dict_parser[ action ] = new_data
218  # @param action name
219  # @param key name of the data
220  # @param new_data to insert into _dict_stock[action]['template'] or ['template']['key']
221  # @throw PortfolioError if wrong action
222  def set_action(self, action, key, new_data):
223  self._logger.debug("set_action, new_data %s" % new_data)
224  assert action != None, 'set_action, action cannot be None'
225 
226  # create on demand with empty dictionary
227  self.get_action( action, key )
228  #d = self.get_action( action, key )
229 
230  # case set_action( action, None, StTmpl.get_template_stock() )
231  # overwrite everything, template and config
232  # crash with string or other, two solutions
233  # 1. hasattr([1,2,3,4], '__iter__')
234  # 2. duck typing
235  try:
236  if 'template' in new_data:
237  self._dict_stock[action] = new_data
238  return self._dict_stock[action]['template']
239  except: #TypeError
240  self._logger.debug("object not iterable")
241  pass
242 
243  # sure it exists, if not empty, overwrite
244  if key is None:
245  self._dict_stock[ action ]['template'] = new_data
246  #d = new_data
247  else:
248  self._dict_stock[ action ]['template'][key] = new_data
249  #d = new_data
250  return self._dict_stock[action]['template']
251 
252  ## @brief return the 'config' dictionary or one of its key from _dict_stock
253  #
254  def get_config( self, action, key=None):
255  self._logger.debug("get_config action %s, key %s" % (action, key))
256  # Do not check if it exists
257  if action in self._dict_stock:
258  if key is None:
259  return self._dict_stock[action]['config']
260  elif key in self._dict_stock[action]['config']:
261  return self._dict_stock[action]['config'][key]
262  # else creates a new key ? maybe later
263  # if action does not exist
264  return None
265 
266  ## @brief query the entry 'config' : 'save' of an action
267  # If specifc is indicated return a boolean value
268  # @code
269  # if stock.saved_as('Info','xml'):
270  # update_xml
271  # @endcode
272  # If specific is None return the list or False
273  # @code
274  # if 'xml' in stock.saved_as('Info'):
275  # update_xml(..)
276  # @endcode
277  #
278  # Check first for the entry in _dict_stock, if not available query the default template through StTmpl.saved_as
279  # @param action
280  # @param specific optional entry, valid values are 'csv' and 'xml'
281  # @return the list of format to save or a boolean
282  def saved_as(self, action, specific=None):
283  # query the local setup of the stock
284  dict_config = self.get_config( action )
285  # None if not existing
286  if dict_config:
287  if specific == None:
288  return dict_config['save']
289  else:
290  # case of Static 'save': False
291  if dict_config['save'] == False:
292  return False
293  # 'save' : [list]
294  if specific in dict_config['save']:
295  return True
296  return False
297  # else query the default template
298  else:
299  return StTmpl.saved_as(action, specific)
300  # only for tests
301  self._logger.error("saved_as Should not arrive here")
302  assert True, "Should not arrive here"
303 
304  ## @brief Return the dictionary of data for XML input/output.
305  # Similar to get_action but templates are extended with sub-dictionary\n
306  # Always return a valid dictionary, maybe empty
307  # @param action name or EAction
308  # @throw PortfolioError if invalid action
309  def get_dict_xml(self, action = None):
310  self._logger.debug("Entry get_dict_xml")
311  # create on demand
312  if self._dict_xml == None:
313  self._dict_xml = {}
314  if action == None:
315  return self._dict_xml
316  if action in self._dict_xml:
317  return self._dict_xml[action]
318  # need to create an entry
319  # check it is valid action
320  e_action = Utils.stringToEAction( action )
321  self._dict_xml[ e_action.name ] = {}
322  return self._dict_xml[ e_action.name ]
323 
324  ## @brief Similar to set_action with extended dictionary.
325  # \pre action cannot be None
326  # first implementation, action is necessary
327  def set_dict_xml(self, action, new_dict_xml ):
328  assert action != None, 'set_dict_xml, action cannot be None'
329  self.get_dict_xml( action )
330  self._dict_xml[ action ] = new_dict_xml
331 
332  ## @brief Return a formatted error.
333  # To generalise to action as well ?
334  # Only convenient way to get them from C++ ?
335  def get_error(self, action = None):
336  self._logger.debug("get_error, action %s", action)
337  #print self._dict_parser
338 
339  if action is not None:
340  return self.get_action( action )['error']
341 
342  # global error, not anymore, only in dict_return_data
343  if self.get_action()['global_error']:
344  self._logger.info("Error in action: %s" % self.get_action()['global_error'])
345  error_action = self.get_action()['global_error']
346  error = self.get_action( error_action )['error']
347  print "Error ", error
348 
349  # transform to string ? more complex structure ?
350  return error
351 
352  #else:
353  # default no error
354  return None
355  ## @}
356 
357  ## @name Functions/properties to access some common data from _dict_parser (Static and InstValue)
358  ## @{
359 
360  ## @brief Getter for the symbol
361  @property
362  def symbol(self):
363  return self._symbol
364 
365  ## @brief Setter raises an exception, it cannot be changed
366  @symbol.setter
367  def symbol(self, value):
368  raise Exception("Cannot change the value of a stock symbol !! stock: %s" % self._symbol)
369 
370  ## @brief Getter function for the state. Possible state OPEN/CLOSED/WAIT_OPEN, default CLOSED.
371  def get_state(self):
372  #print "getter state id ", id(self), " ",self._dict_parser['InstValue']['state']
373  return self.get_action('InstValue','state')
374 
375  def set_state(self, state):
376  #print "setter sate id ", id(self), " ",state
377  self.set_action('InstValue','state',state)
378 
379  # ok working with python 2.7
380  # not sure how to set the property here with @state.setter. only available with python 3...?
381 
382  ## @brief Define both setter and getter, can use stock.state = new_state or print stock.state
383  state = property(get_state, set_state)
384 
385  ## @brief Get the market on which the stock belongs. e.g. US, FR...
386  def get_market( self ):
387  return self.get_action('Static','market')
388 
389  ## @brief Return the last modification of the Instantaneous value
391  return self.get_action('InstValue','last_modification')
392  ## @}
393 
394 # @property
395 # def get_error( self ):
396 # return self.dictstocks[stock]['error']
397 
398 # def set_error(self, error ):
399 # self.dict_parser[stock]['error'] = error
400 
401  ## @brief Function to update Stock with the data extracted by the Parsers.
402  # Update _dict_stock with the new_data from Parser._list_return_data\n
403  # @param dict_new_data dictionary ('template' part only) containing the data to insert, similar template format that Stock._dict_stock
404  # @param source name of the parser used to retrieved the data, inserted in _dict_stock
405  def add_dict_stock(self, dict_new_data, source):
406  self._logger.debug("add_dict_stock() dict_new_data: %s" % dict_new_data )
407  assert ( dict_new_data is not None ),"dict_new_data is not defined in Stock.add_dict_stock"
408 
409  # loop over all actions in dict_new_data (only one action tested)
410  for key,value in dict_new_data.iteritems():
411 
412  # for use with serverportfolio, need to update the state OPEN/WAIT_OPEN/CLOSED
413  if key == "InstValue":
414  self._add_instvalue_data( value )
415 
416  # need to update correctly the date
417  elif key == "HistPrice":
418  self._add_histprice_data( value )
419 
420  # all other, just copy the new data, want to copy others as well
421  else:
422  self._logger.debug("default in add_dict_parser, copy all values, without check")
423  self.set_action(key, None, value)
424 
425  # assign the source, global to each action
426  self.set_action(key,'source', source)
427 
428  self._logger.debug("End add_dict_stock, self.get_action: %s" % (self.get_action()))
429 
430  ## @brief Update InstValue data.
431  # With server running, the state (OPEN/CLOSED) must be updated correctly
432  # @param dict_new_data, templ_action
433  def _add_instvalue_data( self, template_action):
434  self._logger.debug("_add_instvalue_data")
435  self._logger.debug("template_action: %s", template_action)
436  #self._logger.debug("previous data: %s", self.get_action('InstValue'))
437 
438  # eclipse debugging with threads
439  #pydevd.settrace( 5678 )
440 
441  # new, either default CLOSED or previous state
442  prev_state = self.get_action('InstValue','state')
443  # to compare new and previous state
444  new_state = template_action['state']
445 
446  if new_state == 'CLOSED' :
447  if prev_state == 'WAIT_OPEN':
448  self._logger.info("CLOSED and WAIT_OPEN pass, prev_state %s", self.state)
449  pass
450  else:
451  if (prev_state == 'OPEN'):
452  self._logger.info("new_state CLOSED, OPEN->CLOSED %s, copy the state" % (self.symbol))
453  prev_state = new_state
454 
455  # ML WAIT_CLOSED pass if still open (but same value not recorded to file ?
456  # WAIT_OPEN->OPEN
457  elif new_state == 'OPEN':
458  # all case, copy the new_state
459  # self._logger.debug("OPEN copy the state")
460  if prev_state == 'WAIT_OPEN':
461  self._logger.info("WAIT_OPEN->OPEN %s" % self.symbol)
462  prev_state = new_state
463 
464  # update all other values, config was incorrect here, empty{}
465  self.set_action('InstValue', None, template_action)
466  # case wait open bad name, final state better
467  # maybe copy data before the OPEN/CLOSED tests and overwritte in case of WAIT_OPEN
468  self.state = prev_state
469  self._logger.debug("updated inst_value: %s", self.get_action('InstValue') )
470 
471  ## @brief Update Historical Price data.
472  # The field date (last date) must be updated correctly
473  def _add_histprice_data( self, template_action ):
474  self._logger.debug("_add_instvalue_data")
475  #self._logger.debug("previous data: %s", self.get_dict_parser('HistPrice'))
476  # load default template if needed
477  hp = self.get_action('HistPrice')
478  # first_date not changed, but no set up correctly in last_CSV (0)
479  # should correspond to the first loaded date in file now
480  #hp['first_date'] = dict_new_data['HistPrice']['first_date']
481  hp['last_date'] = template_action['last_date']
482  hp['list_csv'] = template_action['list_csv']
483  self._logger.debug('hp %s' % hp)
484 
485  ## @brief Pre-processing only for HistPrice, may extend to all 'csv' later.
486  # The function is called by AbstractParser.store_stock_copy
487  # @param e_action EAction to process
488  def pre_process(self, e_action):
489  self._logger.debug("pre_process e_action.name: %s" % ( e_action.name))
490  # HistPrice, retrieve the date of the last update from the file
491  if ( e_action == EAction.HistPrice ):
492  self.last_CSV( e_action.name )
493 
494  ## @brief Post process the update: check, validate and save the data after one (unique) action is performed.
495  # Delegate this functionality to a ValidStockUpdate object
496  # @param e_action EAction executed by a parser
497  # @param option_post post-process option coming from UpdateStocks/RunParser
498  def post_process(self, e_action, option_post):
499  self._logger.debug("Entry Stock.post_process e_action / option_post: %s / %s " % (e_action, option_post))
500  # extra option parameters
501  interactive = option_post['interactive']
502  to_save = option_post['to_save']
503  # create object, with the tk_manager reference, None if no interactive
504  if self.valid_update is None:
505  self.valid_update = ValidStockUpdate(self,option_post['tk_manager'])
506  try:
507  self.valid_update.post_process_one_action(e_action, option_post)
508  except PortfolioError as ex:
509  self._logger.debug("Caught PortfolioError %s" % ex.get_format_string())
510  raise
511  except Exception as ex:
512  self._logger.debug("Caught Exception %s" % ex)
513  raise
514  # with GSZ HistPrice, example of output without error, to use in test
515  # new_data / _dict_parser {'source': 'YCSV', 'last_date': datetime.datetime(2015, 3, 2, 0, 0),
516  # 'first_date': datetime.datetime(1970, 1, 1, 1, 0),
517  # 'list_csv': [[1425250800, 19.8, 19.86, 19.37, 19.44, 6279800.0], [1424991600, 19.46, 19.88, 19.35, 19.88, 8207000.0], [1424905200, 19.51, 19.74, 19.39, 19.48, 6426300.0], [1424818800, 19.25, 19.45, 19.17, 19.29, 5575500.0], [1424732400, 18.86, 19.35, 18.62, 19.24, 8116600.0], [1424646000, 18.8, 18.84, 18.59, 18.81, 5173700.0], [1424386800, 18.66, 18.74, 18.44, 18.59, 6623200.0], [1424300400, 18.53, 18.69, 18.43, 18.68, 5664000.0], [1424214000, 18.91, 18.98, 18.56, 18.68, 4637300.0], [1424127600, 18.51, 18.76, 18.27, 18.74, 4519600.0], [1424041200, 18.71, 18.72, 18.54, 18.6, 3649000.0], [1423782000, 18.89, 18.92, 18.72, 18.72, 6893900.0], [1423695600, 18.75, 18.86, 18.5, 18.8, 5891100.0], [1423609200, 18.99, 19.0, 18.67, 18.77, 5178200.0], [1423522800, 18.75, 18.99, 18.5, 18.96, 4967600.0], [1423436400, 18.9, 18.94, 18.53, 18.68, 6571700.0], [1423177200, 19.19, 19.21, 19.0, 19.01, 4635800.0], [1423090800, 19.33, 19.4, 19.1, 19.15, 8390900.0], [1423004400, 19.63, 19.73, 19.46, 19.6, 5781600.0], [1422918000, 19.9, 20.05, 19.55, 19.7, 9223500.0], [1422831600, 19.72, 19.88, 19.59, 19.83, 4641200.0]],
518  # 'error': None}
519 
520  ## @name Use C++ wrapPyStock.so library to read/write CSV data to file
521  ## @{
522  ## All functions check for the global variable GlobalDicts.TEST_MODE to write output in tmp directory
523 
524  ## @brief Save instantaneous values into a file.
525  # Use C++ wrapPyStock.so module\n
526  # C++ insures a correct format, correct header, correct directory (including TEST_MODE for debugging and unit-test)
527  #
528  # @todo Be sure only new data are recorded, not done in C++ ? Only check date and values, ok FSLR, not for DJ.
529  def save_inst_value( self ):
530  self._logger.debug("Entry save_inst_value call C++ wrapper stock: %s TEST_MODE: %s " % \
531  (self.symbol, GlobalDicts.TEST_MODE))
532  #print self.print_stock("InstValue")
533  # need to transform in timestamp, equivalent int ? Need util function, check timestamp with python
534  full_datetime = self.get_action('InstValue','date')+" "+ self.get_action('InstValue','time')
535  self._logger.debug("full_datetime %s" % full_datetime)
536  # strptime time return a struct_time
537  time_struct = time.strptime( full_datetime, "%Y-%m-%d %H:%M:%S")
538  #print "time_struct ", time_struct
539  # transform timestamp, epoch for C++
540  date_tmp = int( round(time.mktime(time_struct)))
541  #print 'date_tmp', date_tmp
542  try:
543  #print self.get_action('InstValue','value')
544  #print self.get_action('InstValue','volume')
545  retour = wrapPyStock.SaveInst( self.symbol,[ date_tmp, self.get_action('InstValue','value'), \
546  self.get_action('InstValue','volume') ], GlobalDicts.TEST_MODE )
547  self._logger.debug("after wrapper C++ retour %d", retour)
548  # Make
549  except Exception as ex:
550  self.loogger.debug("Catch Exception in Python: %s", ex)
551  # raise rethrow the same exception, otherwise will just continue
552  raise
553  # send status, no data append. Header dates certianly do not fit
554  if retour != 0 :
555  #self._logger.debug("SaveInst did not append data !!")
556  self._logger.info("SaveInst did not append data !!")
557  return retour
558 
559 # Need to extend with DivSplit, try with an action argument
560 # Saving to file not writen yet, simple file or XML (makes sense to save with other "fundamental" data: SQL also)
561 
562  ## @brief Get the date of the last data from the file saved on disk.
563  # Use C++ code with wrapStock.so, including TEST_MODE variable for unit-test\n
564  # Tested for daily data only.\n
565  #
566  # file searched in /home/michael/workspace_kepler/ROOT_application/data_test/historical/CAC40.DAY.csv (with TEST_MODE)
567  # Store date as datetime in tmeplate HistPrice
568  def last_CSV( self, action ):
569 
570  self._logger.debug("last_CSV symbol %s " % self.symbol )
571  self._logger.debug("action %s" % action)
572 
573  assert( (action == 'HistPrice') | (action == "DivSplit") ),\
574  "last_CSV can be called only with HistPrice or DivSplit"
575 
576  # need to add transform from action name to EAction
577  e_action = Utils.stringToEAction( action )
578  # (re-) initialise
579  #self.set_action( action, None, StTmpl.get_template_stock( e_action))
580  self.get_action( action )
581  self._logger.debug("dict_stock action %s: %s" % (action, self.get_action(action)))
582 
583  # to check if already exists, and up to day
584  if action == 'HistPrice':
585  # To get both, first and last dates !
586  last_date_in_file = wrapPyStock.LastCSVUpdate( self.symbol, GlobalDicts.TEST_MODE )
587  # C++ return always timestamp date
588  self._logger.debug("last_CSV C++ return last_date_in_file %d" % last_date_in_file)
589 
590  # can load in the template, timestamp number of second
591  self.set_action('HistPrice','last_date', datetime.datetime.fromtimestamp( last_date_in_file ))
592  #self.get_dict_parser('HistPrice')['first_date'] = datetime.datetime(0,0,0)
593  self._logger.debug("last date in dict_parser %s" % self.get_action('HistPrice','last_date'))
594 
595  # for unit-test, return timestamp, to delete if no use later
596  return last_date_in_file
597 
598  elif action == 'DivSplit':
599  print "Action DivSplit, get first and last date available TO IMPLEMENT"
600  # default correct
601  #self._dict_parser[ action]['last_date'] = datetime.datetime(0,0,0)
602  #self._dict_parser[ action]['first_date'] = datetime.datetime(0,0,0)
603 
604 # Error with gen_part_of_list_csv, not declared here !!
605  ## @brief Save historical price in CSV format in ROOT_data/hp/symbol.DAY.CSV
606  # Use C++ wrapPyStock.UpdateCSV function. Function improved flexibility can reread/append, deal with partial overlaps.
607  def save_hist_price(self): #interactive ?
608  self._logger.debug("Entry save_hist_price")
609  #self._logger.debug("TEST_MODE %s" % GlobalDicts.TEST_MODE )
610  # check error, here ? before ? otherwise created by default
611  templ_hp = self.get_action('HistPrice')
612  list_csv_memory = self.get_action('HistPrice','list_csv')
613  # a copy of the validated data is created and saved into the file
614  # bug test_stock, list_csv_copy not filled if not interactive
615  list_csv_copy = []
616  if (self._dict_interactive is not None) and ( 'HistPrice' in self._dict_interactive):
617  # shortcut
618  d_inter = self._dict_interactive['HistPrice']
619  # what to send first ? no matter do not enter the function, just assign the generator
620  # work with explicit None
621  sub_list_generator = self.gen_part_of_list_csv(None) #0
622  # here enter
623  tmp = sub_list_generator.send(None)
624  print "tmp ", tmp
625  # easier to sort in inverse for the last key to the first
626  for group in sorted( d_inter.keys(), reverse=True):
627  print "group ", group, d_inter[group]
628  value = d_inter[group]
629  # accepted update, butt only the first/last date are available in dict_directive
630  # could extend with the position in the list list_csv[x:y] easier..., need to be sure not modified
631  if value['repl_add'] == 2:
632  # here it is inverted !!!
633  first_date = Utils.string_to_timestamp( value['first_date'] )
634  last_date = Utils.string_to_timestamp( value['last_date'] )
635  print "first date/last date ", first_date, last_date
636  print "first date/last date ", Utils.timestamp_to_string(first_date), Utils.timestamp_to_string(last_date)
637  # return a list from the previous call to last_date (included)
638  #list_csv_copy.append( sub_list_generator.send( first_date ) )
639  sub_list = sub_list_generator.send( first_date )
640  print "sub_list ", sub_list
641  list_csv_copy += sub_list
642  #list_csv_copy.append()
643  elif value['repl_add'] == 0:
644  print "This group was not validated, stop the copy"
645  break
646  # bug test_stock, list_csv_copy not filled if not interactive
647  else:
648  list_csv_copy = list_csv_memory
649  # NO bug should accept same initial data (overlap 1), Order must be inverted ! No check !
650  # C++ output, find overlap but Data differ ?? they are the same, to check
651  if any(list_csv_copy):
652  retour = wrapPyStock.UpdateCSV( self.symbol, sorted(list_csv_copy, reverse=True), GlobalDicts.TEST_MODE )
653 
654  ## @}
655 
656  ## @brief Compute in how long time the stock will open, to test for setting the state 'WAIT_OPEN' in AutoParsers.
657  # @return datetime a delta time
658  def time_to_open( self ):
659  #if isinstance(self[stock], InvalidStock):
660  # return None
661  market = self.get_market()
662  # Opening time stored in a static dictionary
663  time_str = GlobalDicts.OpeningMarket[market]
664  #print "opening", time_str[0]
665  # print "closing", time_str[1] #not implemented ??
666 
667  # extract the time of the opening from string
668  time_open=time.strptime(time_str[0],"%Hh%Mmn")
669  #print "time_open ", time_open
670 
671  # compute difference of the opening time with the now() time
672  time_now = datetime.datetime.now()
673  # keep identical day with replace
674  # format (2010, 2, 18, 13, 56, 17, 27245),(year,month,day,hour,minute,seconde,ms)
675  time_proba_open = time_now.replace(hour=int(time_open[3]),minute=int(time_open[4]))
676  # time will open in => datetime.deltatime(days,seconds,microseconds)
677  time_to_open = time_proba_open - time_now
678  self._logger.debug("time to open: %s" % time_to_open)
679  return time_to_open
680 
681  ## @brief Check date and time for ML(Marche libre) specifically.
682  #
683  # Stcks from the "Marche Libre" have only a quotation by day (15H30 in Paris).\n
684  # Need only the data-date and time info
685  # If is 'open' assume than work has been done, to check...
686  # @param stock : name of the stock
687  def test_ML_done( self ):
688  self._logger.debug("date %s", self.get_action('InstValue','date') )
689  self._logger.debug("time %s", self.get_action('InstValue','time') )
690  last_date = self.get_action('InstValue','date')
691  date_last = datetime.date(*time.strptime(last_date, "%d/%m/%y")[0:3])
692  today= datetime.date.today()
693  last_time = self.get_action('InstValue','time')
694  time_last= datetime.time(*time.strptime(last_time, "%H:%M:%S")[3:6])
695  now = datetime.time(*time.localtime()[3:6])
696 
697  if (date_last==today) and (now > time_last):
698  return True
699  else:
700  return False
701 
702  ## @brief Create a one line string with the values stored in a dictionary.
703  # If the dictionary is not loaded, return a string message with the symbol and a message starting with 'ERROR'\n
704  # Usually called by DictionaryStocks.make_print_stocks() or UpdateStocks
705  # @param action the EAction name of the data to retrieve
706  # @return a formatted one line string
707  def print_stock( self, action ):
708  # maybe test standard template entry for error
709  # should print if empty, error or invalid
710  # print "to test for error"
711  # assert any( self._dict_stock[action]),"Stock:print_stock action is empty !"
712 
713  # test the data have been updated
714  # always created by get_action, now with default value
715  #if (self.get_action(action)):
716  if action not in self._dict_stock:
717  stock_line = self.symbol + " ERROR NO %s loaded\n" % action
718  return stock_line
719 
720  # sure it exists, make short_cut to action/template
721  sub_data = self.get_action(action)
722  #print sub_data
723 
724 # to add clear error, InvalidStock can be used as well now
725  # Added only in _SetVolatileData, not present if only reading the stock
726  # In fact all ! nothing loaded
727  # if self[stock].has_key('error'):
728  # if self.dictstocks[stock]['error'] != 0 :
729  # stock_line = stock + " ERROR BOURSO\n"
730  # return stock_line
731 
732  if ( action == 'InstValue'):
733  #print "test self.dictstocks ",self.dictstocks
734  stock_line = self.symbol + " " + str(sub_data['value']) + " " + str(sub_data['variation']) + " "
735  stock_line += str(sub_data['time']) + " " + str(sub_data['date']) + " "
736  stock_line += str(sub_data['volume']) + " " + str(sub_data['ouverture']) + " "
737  stock_line += str(sub_data['plushaut']) + " " + str(sub_data['plusbas']) + " "
738  stock_line += str(self.state) + "\n"
739  print "stock_line ", stock_line
740  #return stock_line
741 
742  elif ( action == 'Fundamental'):
743  #sub_data = self.get_dict_parser(action)
744  stock_line = self.symbol + " " + str(sub_data['MarketCapitalization']) + " " + str(sub_data['SharesOwned']) + " "
745  stock_line += str(sub_data['DividendYield']) + " " + str(sub_data['BVPS']) + " " +str(sub_data['PriceBook'])
746  #stock_line += str(sub_data[]) + " " + str(sub_data[]) + " " +str(sub_data[]
747  stock_line += '\n'
748 
749  elif ( action == 'Info'):
750  stock_line = self.symbol + ", " + str(sub_data['Name']) + ", " + str(sub_data['Sector']) + ", "
751  stock_line += str(sub_data['Industry']) + "\n"
752 
753  elif ( action == 'HistPrice'):
754  first_str = sub_data['first_date'].strftime('%Y-%m-%d %H:%M:%S')
755  last_str = sub_data['last_date'].strftime('%Y-%m-%d %H:%M:%S')
756  stock_line = self.symbol + " " + first_str + " " + last_str + "\n"
757 
758  elif (action == 'DatesIntro'):
759  stock_line = self.symbol + " " + sub_data['intro_str'] + "\n"
760 
761  else:
762  stock_line = self.symbol + " ERROR action %s Not implemented in Stock\n" % (action)
763 
764  return stock_line
765 
766 # ########## XML input/output ####################
767 
768 # first test to process all stages of XML data update: read / check_new_data / (validation_stage) /
769 # merge_after_validation / write XML
770 # Focus on Fundamental / Info / (DivSplit ??)
771 
772 # logic similar for HistPrice (looks more DivSplit for validation)
773 # InstValue : cannot validate every 30 sec., may check / clean data before transforming in 5/10/30 minutes
774 # for test/debugging PATH_DATA_ROOT_TEST ?
775 
776  ## @brief ReadXML data from file into internal xml_dict
777  # @note self._dict_xml is (re-)initialised in this function
778  def read_xml(self):
779  self._logger.debug("Entry read_xml")
780 
781  filename = GlobalDicts.PATH_DATA_ROOT + os.sep + "xml" + os.sep + self.symbol + ".xml"
782  self._logger.debug("to read filename %s", filename)
783 
784  if not os.path.isfile(filename):
785  self._logger.debug("File %s does not exist !" % filename )
786  # should create a default XML ? pass
787  #self.get_dict_xml()
788  self._dict_xml = StTmpl.create_default_template_xml()
789  # new
790  #self.set_dict_xml( action, StTmpl.create_default_template_xml() )
791  return
792 
793  # an existing file should be valid...
794  try :
795  # parse return a TreeElement
796  doc = etree.parse( filename )
797  root = doc.getroot()
798 
799  except etree.XMLSyntaxError, e:
800  self._logger.debug("Caught a etree.XMLSyntaxError")
801  raise PortfolioError("PortfolioEror from :%s" % e)
802 
803  print(etree.tostring(root, pretty_print=True))
804 
805  # (re-)initialise the internal dict_xml,get_dict_stock.clear()
806  self.get_dict_xml()
807  print "self.get_dict_xml()", self.get_dict_xml()
808 
809  # test read symbol and compare to symbol
810  # can assert, but not store in dict_xml
811  #self._dict_xml['symbol'] = root.find("symbol").text
812  #assert ( self._dict_xml['symbol'] == self.symbol ), \
813  assert root.find("symbol").text == self.symbol,\
814  "Symbol %s does not correspond in file %s !!" % ( self._dict_xml['symbol'], filename )
815 
816  # loop over all element from root
817  # loop over action childs
818  for el_action in root:
819  #print("%s - %s" % (element.tag, element.text))
820  if el_action.tag == 'symbol':
821  continue
822 
823  print "process the child tag %s", el_action.tag
824  print("%s - %s" % (el_action.tag, el_action.text))
825  try :
826  # create and fill the template_xml
827  dict_action = self._fill_dict_xml( el_action )
828  except PortfolioError as ex:
829  # can use extra_message also
830  hl_exception = PortfolioError("PortfolioError in Stock.ReadXML for symbol: %s\n%s" % (self.symbol,ex))
831  #print "new_traceback:\n", hl_exception.traceback
832  # can modify the traceback ?
833  hl_exception.traceback = hl_exception.traceback + '....\n' + ex.traceback
834  #print "full traceback\n", hl_exception.traceback
835  # type and value given by one instance
836  #print "\n== Raise hl_exception"
837  raise hl_exception
838 
839  # here ok, create a new Exception, value None keep the original(very accurate), traceback is high-level
840  # identical, traceback is re-assigned in Constructor
841  #raise PortfolioError("new PortfolioError throw in ReadXML"), None, sys.exc_info()[2]
842  #raise PortfolioError("new PortfolioError throw in ReadXML")
843  # other possible exception ?
844  except Exception as ex:
845  self._logger.debug("Caught Exception in read_xml")
846  print "sys.exc_info()[2] ", sys.exc_info()[2]
847  print "traceback ", traceback.format_exc(3)
848  raise
849  #print "End get_template, re-raise"
850 
851  # update self
852  self._dict_xml[el_action.tag] = dict_action
853  print "self._dict_xml", self._dict_xml
854  print Utils.pretty_dict( self._dict_xml )
855 
856  ## @brief Create an empty template in _dict_xml and fill with the data in the xml file
857  # @param element etree.Element corresponding to an action
858  def _fill_dict_xml(self, el_action ):
859  self._logger.debug("Entry element tag %s", el_action.tag )
860  # could throw a PortfolioError / XMLError if action does not exist
861  action = el_action.tag
862  # get the template for the action, XML format: template, config, attrib
863  tmp_dict = StTmpl.get_template_xml( action )
864  print "tmp_dict default ", tmp_dict
865  print "attributes action ", el_action.attrib
866  # test, make a full copy, default may be lost
867  tmp_dict['attrib'] = el_action.attrib
868 
869  # loop over child of the action element
870  # now list, should make a loop over Set (getchildren ), findall )
871  for elem_data in el_action:
872  self._logger.debug("elem_data.tag/ text/ attrib: %s / %s / %s" \
873  % (elem_data.tag, elem_data.text, elem_data.attrib))
874  # template for one_value xml, here default (no action)
875  one_value_xml = StTmpl.get_template_one_value_xml()
876  # Maybe update value after setting the attributes, case append True/False
877  one_value_xml['value'] = elem_data.text
878  # to invert the logic ? attributes action may be used as default ?
879  # loop over one_value, check elem_data, else check parent
880  for key_attr in one_value_xml['attrib'].keys():
881  # look if attrib set in the line
882  if key_attr in elem_data.attrib:
883  one_value_xml['attrib'][key_attr] = elem_data.attrib[key_attr]
884  # else look at the parent attrib, can inherit, delete if set explicitely
885  elif key_attr in el_action.attrib:
886  one_value_xml['attrib'][key_attr] = el_action.attrib[key_attr]
887  # else problem, some option not setup !
888  else:
889  assert( 1==1), "One attribute is neither one_value, netiher default action"
890  # append this one_value
891  tmp_dict['template'][ elem_data.tag ].append( one_value_xml )
892 
893  #print "tmp_dict ", tmp_dict
894  print Utils.pretty_dict(tmp_dict)
895  # assign here or return to caller
896  return tmp_dict
897 
898 # action is missing, all dict_stock is checked. Maybe not so good if some stay in memory.
899 #
900 # More convenient for post_process, action executed one by one:
901 #
902 # write another function check_new_data( action, opt_interactive )
903 # -> _valid_xml(action)
904 # -> _valid_list_csv(action)
905 #
906 # if interactive:
907 # ValidTk() or deriv
908 #
909 # same for save_new_data(action)
910 # -> save_xml
911 # -> save hp/div/inst
912 
913 # apply only to XML ?
914 
915  ## @brief Check the validity of the new retrieved data (in _dict_stock), store update in _dict_interactive.
916  # Executed before an automatic or interactive update of the XML data (_dict_xml)\n
917  # Updated data are stored in selff.dict_interactive
918  # @param opt_interactive optional ask validation to user
919 # def check_new_xml_data(self, opt_interactive = False):
920 # self._logger.debug("opt_interactive %s", opt_interactive)
921 #
922 # dict_interactive = {}
923 # # create a list of data to update, need specialization by template ??
924 # # at best, check if some data are present !!
925 # self._logger.debug("_dict_stock: %s", self._dict_stock)
926 #
927 # # check global error ?
928 # #if self.get_dict_parser()['global_error']:
929 # # self._logger.debug("Global Error ", self.get_dict_parser()['global_error'] )
930 #
931 # # loop over the recent action retrieved by parser
932 # for key,value in self.get_action().iteritems() :
933 # self._logger.debug("key/value %s %s" % (key, value))
934 #
935 # # to check for template only, discard config
936 #
937 # # Static/InstValue not updated
938 # # info present in _dict_xml[action][config]
939 # #if (key == 'Static') | (key == 'InstValue') :
940 # # can create on the fly, not really good idea
941 # #if not self.get_dict_xml( key )['config']['save_xml']:
942 #
943 # # can change to to_save
944 # # if not StTmpl.to_save_xml( key ):
945 # # if not self.saved_as( key, 'xml' ):
946 # # continue
947 #
948 # if self.saved_as( key, 'xml' ):
949 # # should not throw exception if possible, better error for interactive mode
950 # dict_action_inter = self._valid_new_value_xml( key, opt_interactive )
951 #
952 # if any(dict_action_inter):
953 # dict_interactive[ key ] = dict_action_inter
954 #
955 # #else pass
956 
957 # keep as example
958 # fname = "check_data_tmpl_" + key
959 # # throw AttributeError (getattr)
960 # try:
961 # getattr(self, fname)( list_interactive, opt_interactive ) # key in the funct. name
962 # except Exception as ex:
963 # self._logger.error("Caught Exception: %s", ex)
964 # self._logger.error("Function %s not implemented " % fname)
965 # raise
966 #
967 # self._logger.debug("\nFinal dict_interactive:\n%s" % dict_interactive)
968 # print Utils.pretty_dict( dict_interactive )
969 # self._dict_interactive = dict_interactive
970 
971  ## @brief Final merge (after user/auto validation) and write XML file.
972  # This step includes the merging of the data in self.dist_interactive,\n
973  # and (re-)write the file by a call to write_xml, include TEST_MODE for unit-test
974 # def save_new_xml_data(self):
975 # self._logger.debug("Entry save_new_data_to_xml")
976 #
977 # # loop over the data in dict_interactive (have been validated)
978 # for (key_action, value_action) in self._dict_interactive.iteritems():
979 # self._logger.debug("key_action/value_action %s / %s" % (key_action, value_action))
980 #
981 # # dictionary to update
982 # dict_xml = self.get_dict_xml( key_action )
983 #
984 # # for each eaction loop over key_data and value_data,
985 # # check if modifed to 0 or 2
986 # for (key_data, value_data) in value_action.iteritems():
987 # self._logger.debug("\nkey_data / value_data %s / %s" % (key_data, value_data))
988 #
989 # # certainly to save, or to write into file for feedback
990 # if value_data['repl_add'] > 0:
991 # self._logger.info("update xml value / key_data : %s / %s / %s " % \
992 # (self.symbol, key_data, value_data['new_value']) )
993 #
994 #
995 # # value can overwite the default, use the last one ...?
996 # #b_append_data = bool( dict_xml['template'][key_data][-1]['attrib']['append'] )
997 # # Not working if new created file from template
998 # #if dict_xml['template'][key_data][-1]['attrib']['append'] == "True":
999 # if dict_xml['template'][key_data][-1]['attrib']['append'] == True:
1000 # #b_append_data = True
1001 # print "append data"
1002 # new_tmpl_value = StTmpl.get_template_one_value_xml()
1003 # new_tmpl_value['value'] = value_data['new_value']
1004 # new_tmpl_value['attrib']['date'] = value_data['date']
1005 # new_tmpl_value['attrib']['source'] = value_data['source']
1006 # new_tmpl_value['attrib']['append'] = 'True'
1007 # # append to the list
1008 # dict_xml['template'][ key_data ].append( new_tmpl_value )
1009 #
1010 # # replace the last value, do not modify the previous entries
1011 # else:
1012 # dict_xml['template'][ key_data ][-1]['value'] = value_data['new_value']
1013 # # format timestamp for C++, transform on the fly ? date added automatically
1014 # dict_xml['template'][ key_data ][-1]['attrib']['date'] = value_data['date']
1015 # dict_xml['template'][ key_data ][-1]['attrib']['source'] = value_data['source']
1016 #
1017 # # delete entry ? just pass
1018 # #else:
1019 #
1020 # self._logger.debug("dict_xml before wirte_xml:\n%s " % Utils.pretty_dict(self.get_dict_xml()) )
1021 # self._logger.debug("dict_xml before wirte_xml:\n%s " % (self.get_dict_xml()) )
1022 # self.write_xml()
1023 
1024  ## @brief Write the data stored in _dict_xml into a XML file.
1025  # At this point, write only the data with a valid 'source', avoid to write the default template.\n
1026  # No parameter action, want to keep all previous and valid entries
1027  def write_xml(self):
1028  self._logger.debug("Entry write_xml")
1029  self._logger.debug("TEST_MODE %s" % GlobalDicts.TEST_MODE )
1030 
1031  # can happen ? not a nomal use
1032  if not any(self.get_dict_xml()):
1033  self._logger.error("write xml with an emptyy dict_xml, do nothing")
1034  return
1035 
1036  # should save in xml_tmp in test
1037  if GlobalDicts.TEST_MODE :
1038  filename = GlobalDicts.PATH_DATA_ROOT_TEST + os.sep + "xml_tmp" + os.sep + self.symbol + ".xml"
1039  else :
1040  filename = GlobalDicts.PATH_DATA_ROOT + os.sep + "xml" + os.sep + self.symbol + ".xml"
1041  self._logger.debug("to write filename %s", filename)
1042  # maybe backup ?
1043  self._logger.debug("Create a new XML file")
1044 
1045  # create a root Element
1046  root = etree.Element("root")
1047  doc = etree.ElementTree(root)
1048 
1049  # child symbol, not necessary, can check it is correct in reading...
1050  # always present
1051  ch_symbol = etree.SubElement(root, "symbol")
1052  ch_symbol.text = self.symbol
1053 
1054  # loop over the action
1055  for key_action, value_action in self.get_dict_xml().iteritems():
1056  self._logger.debug("key_action %s" % key_action)
1057 
1058  # symbol still present, if dict_xml read from file. No need to store (only assert in read_xml is enought
1059  #if key_action == 'symbol':
1060  # continue
1061 
1062  # set for all actions, still neeeded ? in case of modification default maybe ?
1063  # user can modify this ??? not implemented, if in write_xml should be fine
1064  #if not self.get_dict_xml( key_action )['config']['save_xml']:
1065  # self._logger.info("save_xml True for stock %s. Does the original setup have been modified ?")
1066  # continue
1067 
1068  # create sub-tree action in helper function
1069 
1070  # if create default template for all actions when creating a new file, DivSplit not updated
1071  # now version only one action, still problem if new xml file/can check source
1072  # need to know if values has been updated by a parser, or print all default
1073 
1074  # test is wrong, if source is correct, data have been updated, or add an argument in write_xml
1075  #if 'attrib' in value_action['config']:
1076  # main attrib, for each action
1077  if 'attrib' in value_action:
1078  #if value_action['config']['attrib']['source'] != None:
1079  if value_action['attrib']['source'] != None:
1080  sub_tree_action = self._create_xml_action( key_action, value_action )
1081  root.append( sub_tree_action )
1082 
1083 # need more test, need maybe a generator, get back next item and call self.create_xml_action
1084 # what to do if intermediate result ( root.append( sub_tree_action ) )
1085 # this way implement only for function for value
1086 # sub_tree_action = self._apply_loop_dict( self.create_xml_action )
1087 
1088  print(etree.tostring(root, pretty_print=True))
1089 
1090  self._logger.debug("write the file")
1091  with open( filename , 'w') as out_file:
1092  doc.write(out_file, pretty_print=True)
1093 
1094  # could be static, or pass the sub_dictionary only
1095  ## @brief Helper function, generate xml sub-tree for one action
1096  # @param key_action action name
1097  # @param value_action associated dictionary to the dictionary key key_action
1098  def _create_xml_action(self, key_action, value_action ):
1099  self._logger.debug("Entry %s %s", sys._getframe().f_code.co_name, self.symbol )
1100  self._logger.debug("key_action %s" % key_action)
1101  # if create default template for all actions when creating a new file, DivSplit not updated
1102  # create sub_tree for the action
1103  action_tree = etree.Element( key_action )
1104  # deal with attribute of the action root
1105  action_attr = action_tree.attrib
1106  #for key_attr,value_attr in value_action['config']['attrib'].iteritems():
1107  for key_attr,value_attr in value_action['attrib'].iteritems():
1108  action_attr[key_attr] = str(value_attr)
1109 
1110  b_action_append = action_attr['append']
1111  b_action_source = action_attr['source']
1112 
1113  # dict_xml, values_data always in a list
1114  for key_data, values_data in value_action['template'].iteritems():
1115  # never print these entries
1116  if (key_data == "error") | (key_data == "other"):
1117  continue
1118  # values_data is a list, StockTemplate._templ_one_value_xml, only last one interested here ?
1119  for one_value_data in values_data:
1120  print "one_value_data ", one_value_data
1121  child = etree.SubElement(action_tree, key_data)
1122  # to change float to string ? certainly better ?
1123  child.text = str(one_value_data['value'])
1124  one_value_attr = child.attrib
1125  #one_value_attrib : date / source / append
1126  for key_attr in one_value_data['attrib']:
1127  # always print the date
1128  if key_attr == 'date':
1129  one_value_attr[key_attr] = str(one_value_data['attrib'][key_attr])
1130  continue
1131  # case source, print only if different from default
1132  if key_attr == 'source':
1133  if str(one_value_data['attrib'][key_attr]) != str(b_action_source):
1134  one_value_attr[key_attr] = str(one_value_data['attrib'][key_attr])
1135  # else use default
1136  continue
1137  if key_attr == 'append':
1138  if str(one_value_data['attrib'][key_attr]) != str(b_action_append):
1139  one_value_attr[key_attr] = str(one_value_data['attrib'][key_attr])
1140  # print "append is True"
1141  #else:
1142  #print "append is False"
1143  else:
1144  print "one_value_xml do not repeat attribute %s" % key_attr
1145  print(etree.tostring(action_tree, pretty_print=True))
1146  return action_tree
1147 
1148 # #### maybe convenient, loop over action, loop over value, apply specific function f_todo (opt, loop over ValidXML template)
1149 # tricky for retour, cannot insert intermediate. Need a generator ? thread-safe ?
1150 # def _apply_loop_dict(self, f_todo):
1151 # self.logger.debug("Entry %s %s", sys._getframe().f_code.co_name, self.symbol )
1152 # print "f_todo ", f_todo
1153 # #print "value ", value
1154 #
1155 # for key_action, value_action in self.get_dict_xml().iteritems():
1156 # print "key_action ", key_action
1157 # print "value_action"
1158 #
1159 # if key_action == 'symbol':
1160 # continue
1161 #
1162 # retour = f_todo( key_action, value_action )
1163 #
1164 # return retour
1165 
1166 # case of interactive, need to modify self._dict_parser, list of all data to update
1167 # or during selection delete the entry we do not want to add, easier for listCSV
1168 # all data correct in self._dict_parser
1169 
1170 # namedtuple can avoid nested dictionary
1171 # http://stackoverflow.com/questions/12119612/nicer-way-to-iterate-to-dictionary-in-python-to-avoid-many-nested-for-loops
1172 
1173 # choice, I pass opt_interactive again, or if interactive false all values 1 are treated 0 before
1174 
1175  # brief Last step to merge the data present in _dict_parser to _dict_xml.
1176  # The replacement is done only for the data present in list_interactive after potential user validation.
1177  # If non interactive mode, data have been discarded in list_update_xml_value_generic. Just need to check > 0
1178 # def _merge_data_to_xml(self):
1179 # self.logger.debug("Entry _merge_data_to_xml")
1180 
1181 ## @class InvalidStock
1182 # @brief Class template for invalid stocks, if a stock symbol is not found in the configuration file.
1183 # If mistakes are done in some input, the code can continue to run without breaking.
1184 # Test could be done in DictStocks for errors.
1186 
1187  ## @brief Constructor.
1188  # Need the invalid template in creation, could be hard-coded in init
1189  def __init__(self, symbol, dict_static_invalid ):
1190  # init base, with specific static dictionary in entry
1191 
1192  # modif in templates, need tmp
1193  tmp = StTmpl.get_template_stock('Static')
1194  tmp['template'] = StTmpl.get_tmpl_static_invalid()
1195  #super(InvalidStock, self).__init__(symbol, dict_static_invalid)
1196  super(InvalidStock, self).__init__(symbol, tmp)
1197  # own logger
1198  self._logger = logging.getLogger('SP.InvalidStock')
1199  self._logger.warning("Init InvalidStock %s" % symbol)
1200 
1201  print "init Invalid, dict_static_invalid", dict_static_invalid
1202  print "tmp ", tmp
1203 
1204  def is_valid(self):
1205  return False
1206 
1207  ## @brief Override method of base class, return None
1208  def time_to_open(self):
1209  return None
1210 
1211  ## @brief Override method of base class, return string containing ERROR.
1212  def print_stock(self, action):
1213  return "%s ERROR InvalidStock\n" % self.symbol
1214 
1215 if __name__ == "__main__":
1216  # only for test, until now Dictionary not imported, circular import
1217  # http://effbot.org/zone/import-confusion.htm
1218  from serverportfolio.DictionaryStocks import DictionaryStocks
1219 
1220  logging.basicConfig(level=logging.DEBUG)
1221  logging.getLogger('SP')
1222  m_logger = logging.getLogger("SP.main")
1223 
1224  print "Main Stock.py"
1225  # create a Stock object
1226  #stock = Stock('CAC40', DictionaryStocks().get_dict_static('CAC40'))
1227  m_stock = Stock('GSZ', DictionaryStocks().get_dict_static('GSZ'))
1228  print "m_stock._dict_stock ", m_stock._dict_stock
1229 
1230  # some access to the static data
1231  print "symbol: ", m_stock.symbol
1232  print "market: ", m_stock.get_market()
1233  print "dict_stock: ", m_stock.get_action()
1234  print "dict_stock[static] ", m_stock.get_action('Static')
1235 
1236  print "\n== last_CSV deactivated for dev."
1237  #print "date ", stock.last_CSV()," string ",datetime.datetime.fromtimestamp( stock.last_CSV() ).strftime('%Y-%m-%d %H:%M:%S')
1238  #print "date string ", datetime.datetime.fromtimestamp( stock.last_CSV('HistPrice') ).strftime('%Y-%m-%d %H:%M:%S')
1239 # try :
1240 # stock.last_CSV('HistPrice')
1241 # except PortfolioError as ex:
1242 # print "Caught a PortfolioError "
1243 # print ex.get_format_string()
1244 # sys.exit(1)
1245 # print "date string ", stock.get_dict_parser('HistPrice')['last_date'].strftime('%Y-%m-%d %H:%M:%S')
1246 
1247  # dict_return_data a bit more complex
1248  # config are missing here ? but seems correct in output !
1249  m_dict_info = {
1250  'Name' : 'GDF-Suez', #str(),
1251  'Sector' : 'Energy', # str(),
1252  'Industry' : 'EnergyGaz', #str(),
1253  'StockExchange' : 'Paris', #str(),
1254  # variable, YQL may report other entries
1255  'other' : {},
1256  'error' : None,
1257  # source must be present
1258  'source' : 'YQL'
1259  }
1260  m_stock.set_action( 'Info', None, m_dict_info)
1261 
1262  # should use template function for correctness, keep explicit for unit-test
1263  m_dict_fund = {
1264  'MarketCapitalization' : float(20000),
1265  'SharesOwned' : int(15000),
1266  'DividendYield' : float(3.5),
1267  'BVPS' : float(5.5),
1268  'PriceBook' : float(3.0),
1269  'PER' : float(0),
1270  'PEG' : float(1.0),
1271  # variable field, only YQL may report other entry
1272  'other' : {},
1273  'error' : None,
1274  'source' : 'YQL'
1275  }
1276  m_stock.set_action('Fundamental', None, m_dict_fund)
1277 
1278  #dict_div = {
1279  # 'first_date' : '2015-01-01',
1280  # 'last_date' : '20115-02-01',
1281  # 'list_div' :
1282  #}
1283  print "_dict_stock ", m_stock.get_action()
1284  print Utils.pretty_dict( m_stock.get_action() )
1285 
1286  #print "\n== WriteXML 1"
1287  #m_stock.writeXML()
1288 
1289  print "\n== read_xml"
1290  try :
1291  m_stock.read_xml()
1292 
1293  except PortfolioError as ex:
1294  print "Caught PortfolioError in main, ex:\n ", ex
1295  print "\n",ex.get_format_string()
1296  sys.exit(1)
1297 
1298  except Exception, ex:
1299  print "Caught an Exception ", ex
1300  print "sys.exc_info()[2] ", sys.exc_info()[2]
1301  print "traceback ", traceback.format_exc(3)
1302  sys.exit(1)
1303 
1304 # print "\n== write_xml"
1305 # try :
1306 # m_stock.write_xml()
1307 #
1308 # except PortfolioError as ex:
1309 # print "Caught PortfolioError in main, ex:\n ", ex
1310 # print "\n",ex.get_format_string()
1311 # sys.exit(1)
1312 #
1313 # except Exception, ex:
1314 # print "Caught an Exception ", ex
1315 # print "sys.exc_info()[2] ", sys.exc_info()[2]
1316 # print "traceback ", traceback.format_exc(3)
1317 # sys.exit(1)
1318 #
1319 # sys.exit(1)
1320 
1321  print "\n==Interactive simulation"
1322  print "_dict_parser:"
1323  print Utils.pretty_dict( m_stock.get_action() )
1324 
1325  try :
1326  # test update of mutliple action
1327  m_stock.check_new_xml_data( opt_interactive = False )
1328  # should test complete post_process function
1329  # m_stock.post_process(e_action, option_post)
1330  except Exception as ex:
1331  print "Caught an Exception from checkNewData", ex
1332  #print "sys.exc_info()[2] ", sys.exc_info()[2]
1333  print "traceback ", traceback.format_exc(3)
1334 
1335  #sys.exit(1)
1336  #print "dict_parser"
1337  #print Utils.pretty_dict( m_stock.get_dict_parser )
1338  #for dict in m_stock.list_interactive
1339  #print self.get_dict_parser( dict_action_list.keys()[0])
1340  #sys.exit(1)
1341 
1342  # if interactive == False, print directly the data to the file
1343  try :
1344  m_stock.save_new_xml_data()
1345 
1346  except PortfolioError as ex:
1347  print "Caught PortfolioError ", ex
1348  print ex.get_format_string()
1349 
1350  except Exception as ex:
1351  print "Caught an Exception from save_new_data", ex
1352  # print "sys.exc_info()[2] ", sys.exc_info()[2]
1353  print "traceback ", traceback.format_exc(3)
1354 
1355  print "\n== Create new XML"
1356  m_stock = Stock("FP", DictionaryStocks().get_dict_static('FP'))
1357  m_stock.read_xml()
1358 
1359  print "\n== Write new empty XML"
1360  m_stock.write_xml()
1361 
1362 # print '\n== Test property on state\n'
1363 # m_stock._dict_parser[ 'InstValue' ] = { 'state' : 'CLOSED' }
1364 # print m_stock.get_dict_parser()
1365 # print 'initial state ', m_stock.state #get_state() #state
1366 # print "Open the stock"
1367 # m_stock.state = 'OPEN' #
1368 # print 'final state ', m_stock.state #get_state() #state
1369 # print "modify state "
1370 # m_stock._dict_parser[ 'InstValue' ]['state'] = 'CLOSED'
1371 # print "real ", m_stock._dict_parser[ 'InstValue' ]['state']
1372 # print "wrong ", m_stock.state #get_state()
1373 #
1374 # print "init symbol ", m_stock.symbol
1375 # m_stock.symbol = 'toto' ### Error cannot change symbol
1376 # print "new symbol", m_stock.symbol
def get_last_modification
Return the last modification of the Instantaneous value.
Definition: Stock.py:390
Base class of the custom exceptions.
def set_action
Fill _dict_stock['action'] with new_data.
Definition: Stock.py:222
def get_state
Getter function for the state.
Definition: Stock.py:371
Define class ValidStockUpdate.
def add_dict_stock
Function to update Stock with the data extracted by the Parsers.
Definition: Stock.py:405
def is_valid
Return if the Stock is valid.
Definition: Stock.py:124
def time_to_open
Override method of base class, return None.
Definition: Stock.py:1208
def _fill_dict_xml
Create an empty template in _dict_xml and fill with the data in the xml file.
Definition: Stock.py:858
_dict_interactive
Store old/new values before update which may be applied to XML.
Definition: Stock.py:97
def get_error
Return a formatted error.
Definition: Stock.py:335
def set_dict_xml
Similar to set_action with extended dictionary.
Definition: Stock.py:327
def save_inst_value
Save instantaneous values into a file.
Definition: Stock.py:529
def get_action
Return the action template part from _dict_stock or one of its data value.
Definition: Stock.py:182
Define the global variable StockTemplates.StTmpl and dictionary templates.
def get_market
Get the market on which the stock belongs.
Definition: Stock.py:386
def _add_instvalue_data
Update InstValue data.
Definition: Stock.py:433
Class template for invalid stocks, if a stock symbol is not found in the configuration file...
Definition: Stock.py:1185
def __init__
Constructor.
Definition: Stock.py:75
Define custom and specific exceptions for the complete package.
def get_config
return the 'config' dictionary or one of its key from _dict_stock
Definition: Stock.py:254
def print_stock
Create a one line string with the values stored in a dictionary.
Definition: Stock.py:707
def get_dict_xml
Return the dictionary of data for XML input/output.
Definition: Stock.py:309
Extend class Stock with functions related to the validation and saving after a stock update: most of ...
_dict_stock
Set of templates to store results from parsers, include 'Static' data.
Definition: Stock.py:83
def write_xml
Check the validity of the new retrieved data (in _dict_stock), store update in _dict_interactive.
Definition: Stock.py:1027
def read_xml
ReadXML data from file into internal xml_dict.
Definition: Stock.py:778
def last_CSV
Get the date of the last data from the file saved on disk.
Definition: Stock.py:568
Store data and functions related to one stock.
Definition: Stock.py:63
state
Define both setter and getter, can use stock.state = new_state or print stock.state.
Definition: Stock.py:383
Container of all Stocks objects, it also reads the static stocks configuration file "dictstocks...
def post_process
Post process the update: check, validate and save the data after one (unique) action is performed...
Definition: Stock.py:498
Global variables for configuration: paths, TCP ports and generic definitions.
Definition: GlobalDicts.py:1
def test_ML_done
Check date and time for ML(Marche libre) specifically.
Definition: Stock.py:687
def print_stock
Override method of base class, return string containing ERROR.
Definition: Stock.py:1212
def time_to_open
Compute in how long time the stock will open, to test for setting the state 'WAIT_OPEN' in AutoParser...
Definition: Stock.py:658
def symbol
Getter for the symbol.
Definition: Stock.py:362
def _create_xml_action
Helper function, generate xml sub-tree for one action.
Definition: Stock.py:1098
def _add_histprice_data
Update Historical Price data.
Definition: Stock.py:473
_dict_xml
Dictionary for storing data for XML input/output, extends _dict_parser templates. ...
Definition: Stock.py:91
valid_update
Validation is done by a ValidStockUpdate object.
Definition: Stock.py:99
def saved_as
query the entry 'config' : 'save' of an action If specifc is indicated return a boolean value ...
Definition: Stock.py:282
_symbol
Main key for all dictionaries.
Definition: Stock.py:80
Define singleton class DictionaryStocks, act as the main container of Stocks objects.
def to_list
Similar to Utils.to_list but deals with Stock or list of Stocks.
Definition: Stock.py:107
def pre_process
Pre-processing only for HistPrice, may extend to all 'csv' later.
Definition: Stock.py:488
def save_hist_price
Save historical price in CSV format in ROOT_data/hp/symbol.DAY.CSV Use C++ wrapPyStock.UpdateCSV function.
Definition: Stock.py:607