esurfer 9 роки тому
батько
коміт
de6e118e36

+ 95 - 2
finopt/opt_serve.py

@@ -345,7 +345,7 @@ class QServer(object):
         strikes =[e for e in Set(map(lambda x:x[4], plist))]
         # sort the months in ascending order
         months = sorted([e for e in Set(map(lambda x:x[2], plist))])
-	print klist
+        print klist
         print strikes
         # print months
         # print len(klist), len(s)
@@ -396,6 +396,96 @@ class QServer(object):
         return s    
     
     
+    def generate_garray_ex(self, plist):
+    
+    #[['HSI', 'FUT', '20170126', 'None', '0', '50.0', '0.0000', '0.0000'], 
+    # ['HSI', 'OPT', '20170126', 'C', '23000', '50.0', '-4.0000', '3240.3350'], ['HSI', 'OPT', '20170126', 'P', '22600', '50.0', '-1.0000', '1330.9600'], ['HSI', 'OPT', '20170227', 'C', '23400', '50.0', '-2.0000', '9880.9600'], ['HSI', 'OPT', '20170227', 'C', '23600', '50.0', '-1.0000', '9530.9600'], ['HSI', 'OPT', '20170227', 'P', '20200', '50.0', '-3.0000', '980.9600'], ['HSI', 'OPT', '20170227', 'P', '20400', '50.0', '-1.0000', '7664.2933'], ['HSI', 'OPT', '20170227', 'P', '21800', '50.0', '-3.0000', '5197.6267'], ['MHI', 'FUT', '20170126', 'None', '0', '10.0', '5.0000', '230459.6680']]
+        
+        
+        # generate a key map with month-right-strike
+        # example: ('20160330-C-20000', 0.2),...
+        
+        #klist = map(lambda x: ('%s-%s-%s' % (x[2], x[3] if x[3] <> 'None' else 'F', x[4]), float(x[5])/50.0*float(x[6])), plist)
+        
+        # 2017 - handle futures price 
+        
+        def getX(x):
+            val = x[4] if x[3] <> 'None' else str(int(round(float(x[7])/ float(x[5]),-1)))
+    #        print val
+            return val
+        
+        plist = filter(lambda x: int(float(x[6])) <> 0, plist)
+        klist = map(lambda x: ('%s-%s-%s' % (x[2], x[3] if x[3] <> 'None' else 'F', getX(x)), float(x[5])/50.0*float(x[6])), plist)
+        # for e in sorted(klist):
+        #     print e
+    
+        # get the unique keys in klist
+        unique_keys= Set(map(lambda x:x[0], klist))
+        
+        
+        #strikes =[e for e in Set(map(lambda x:x[4], plist))]
+        # 2017 
+        strikes = [e for e in Set(map(lambda x:getX(x), plist))]
+        
+         
+        # sort the months in ascending order
+        months = sorted([e for e in Set(map(lambda x:x[2], plist))])
+        print klist
+        print strikes
+        # print months
+        # print len(klist), len(s)
+        
+        # group and sum position by month, strike, right
+        grouped_pos = []
+        for elem in unique_keys:
+            grp1 = filter(lambda x: x[0] == elem, klist)
+            print grp1
+            # sum items with same key
+            # example: [('20160330-P-19600', -1.0), ('20160330-P-19600', 0.2)]
+            grouped_pos.append( grp1[0] if len(grp1) == 1 else reduce(lambda x,y: (x[0], x[1]+y[1]), grp1) )
+            print '---'
+        
+        print grouped_pos    
+            
+        garr = {}
+        def init_garray(x):
+            garr[x] = {}
+        map(init_garray, sorted(strikes))
+        print garr
+        
+        def set_garray(x):
+            vals = x[0].split(('-'))
+            
+            if vals[0] == months[0]:
+                
+                if vals[1] == 'C':
+                    garr[vals[2]]['NEAR_C'] = x[1]
+                elif vals[1] =='P':
+                    garr[vals[2]]['NEAR_P'] = x[1]
+                else:
+                    garr[vals[2]]['NEAR_F'] = x[1]
+            elif vals[0] == months[1]:
+        
+                if vals[1] == 'C':
+                    garr[vals[2]]['FAR_C'] = x[1]
+                elif vals[1] =='P':
+                    garr[vals[2]]['FAR_P'] = x[1]
+                else:
+                    garr[vals[2]]['FAR_F'] = x[1]
+        # find all C of near month
+        map(set_garray, grouped_pos)
+        print garr
+        s=''
+        for k, v in garr.iteritems():
+            s+= '[%s, %s,%s,%s,%s,%s,%s],' % (k, v['NEAR_P'] if 'NEAR_P' in v else '0',
+                                         v['NEAR_C'] if 'NEAR_C' in v else '0',  
+                                         v['FAR_P'] if 'FAR_P' in v else '0', 
+                                         v['FAR_C'] if 'FAR_C' in v else '0', 
+                                         v['NEAR_F'] if 'NEAR_F' in v else '0', 
+                                         v['FAR_F'] if 'FAR_F' in v else '0', )
+                                         
+        return s        
+    
     @cherrypy.expose
     def ws_position_chart_ex(self):
         p = portfolio.PortfolioManager(config)
@@ -404,7 +494,10 @@ class QServer(object):
         f = open(opt_pos_chart_tmpl)
         html_tmpl = f.read()
 
-        html_tmpl = html_tmpl.replace('{{{dataPCpos}}}', self.generate_garray(p.get_tbl_pos_list()))
+        #html_tmpl = html_tmpl.replace('{{{dataPCpos}}}', self.generate_garray(p.get_tbl_pos_list()))
+        # 2017 - handles futures
+
+        html_tmpl = html_tmpl.replace('{{{dataPCpos}}}', self.generate_garray_ex(p.get_tbl_pos_list()))
         
         html_tmpl = html_tmpl.replace('{{{dataTablePos}}}', p.get_tbl_pos_csv())
         

+ 7 - 2
finopt/optcal.py

@@ -105,7 +105,10 @@ def get_hk_holidays(year):
         if str(year) == (Date().todaysDate() + Period("1y")).ISO()[0:4]:
             url = 'http://www.gov.hk/en/about/abouthk/holiday/'
         else:
-            url = 'http://www.gov.hk/en/about/abouthk/holiday/{{year}}.htm'
+            #url = 'http://www.gov.hk/en/about/abouthk/holiday/{{year}}.htm'
+	    # lc 2017-01-03
+	    # just use the same url for both cases
+            url = 'http://www.gov.hk/en/about/abouthk/holiday/'
             url = url.replace('{{year}}', str(year))
 
 
@@ -326,4 +329,6 @@ if __name__ == '__main__':
     for i in month_names:
         dd = get_HSI_last_trading_day(holidays, i, 2017)
         print dd
-        
+        
+    print holidays	
+    print get_HSI_last_trading_day(['20170128'], 1, 2017)

BIN
finopt/optcal.pyc


+ 12 - 4
finopt/options_data.py

@@ -188,12 +188,13 @@ class OptionsMarketDataManager():
         
         def derive_mid_price(field_id):
             bidask = 2 if (field_id == 1) else 1
+	    logging.debug("********** dump %s" % str(DataMap().get(msg.tickerId)))
             if bidask in DataMap().get(msg.tickerId):
                 if DataMap().get(msg.tickerId)[bidask] > 0.0:
                     mid = (DataMap().get(msg.tickerId)[bidask] + msg.price) / 2
                 else:
                     mid = msg.price
-            logging.debug("OptionsMarketDataManager: incoming price field%d == %0.4f datamap field%d ==2 %0.4f"\
+            	logging.debug("OptionsMarketDataManager: incoming price field%d == %0.4f datamap field%d ==2 %0.4f"\
                            % (field_id, msg.price, bidask, DataMap().get(msg.tickerId)[bidask]))                                 
             return mid
         
@@ -549,7 +550,9 @@ class DataMap():
         holiday_key = '%s%s' % (holiday_key_prefix, year)
         holidays = rs.get(holiday_key)
                     
-        if holidays == None:
+#        if holidays == None:
+#	fix 2017-01-03
+        if holidays == 'null':
             holidays = optcal.get_hk_holidays(year)
             logging.info("options_data:set_option_calendar: retrieved from gov.hk --> update Redis: [%s]: %s", holiday_key, json.dumps(holidays))
             rs.set(holiday_key, json.dumps(holidays))    
@@ -563,7 +566,12 @@ class DataMap():
         undly_months_prices = eval(config.get("market", "option.underlying.month_price").strip('"').strip("'"))
         
         undly_months_prices[0][0] = optcal.get_HSI_last_trading_day(holidays, month, year)
-        undly_months_prices[1][0] = optcal.get_HSI_last_trading_day(holidays, (month + 1) % 12, year)
+	next_month = (month + 1) % 12 if (month + 1) % 12 <> 0 else (month + 1) % 12 + 12
+#2016/12 lc - fix year end bug 
+	year = year if next_month <> 1 else year + 1
+	
+        logging.info("options_data:set_option_calendar: next month: year %s:%s " % (next_month, year))
+        undly_months_prices[1][0] = optcal.get_HSI_last_trading_day(holidays, next_month, year)
         
         logging.info("options_data:set_option_calendar:  %s " % str(undly_months_prices))
         return undly_months_prices
@@ -1100,7 +1108,7 @@ def add_portfolio_subscription(config, omd):
     p = portfolio.PortfolioManager(config)
     p.retrieve_position()
     
-    
+    logging.debug("************ complete retrieve pos ****") 
 #    undly_months_prices = eval(config.get("market", "option.underlying.month_price").strip('"').strip("'"))
     # 20160612
     undly_months_prices = DataMap().rskeys['option.underlying.month_price']

+ 270 - 49
finopt/portfolio.py

@@ -340,7 +340,7 @@ class PortfolioManager():
         # 2016/06/11
         # add 2 new columns: avgcost in points, unreal_pl ratio
         #s = '["symbol","right","avgcost","spotpx","pos","delta","theta","pos_delta","pos_theta","unreal_pl","last_updated"],'
-        s = '["symbol","right","avgcost","avgpx","spotpx","pos","delta","theta","pos_delta","pos_theta","unreal_pl","unreal%","last_updated"],'
+        s = '["symbol","right","avgcost","avgpx","spotpx","pos","delta","theta","pos_delta","pos_theta","unreal_pl","%gain/loss" ],'
         
         def split_toks(x):
             try: # 
@@ -348,20 +348,28 @@ class PortfolioManager():
                 #print pmap
                 gmap = json.loads(self.r_conn.get(x[3:]))
                 #print gmap
-#                 s = '["%s","%s",%f,%f,%f,%f,%f,%f,%f,%f,"%s"],' % (x[3:], x[len(x)-1:], pmap['6001'], gmap['5006'], pmap['6002'],\
-#                                                                              gmap['5002'],gmap['5004'],\
-#                                                                              pmap['6005'],pmap['6006'],pmap['6008'],pmap['last_updated'])
-        # 2016/06/11        
-                s = '["%s","%s",%f,%f,%f,%f,%f,%f,%f,%f,%f,%f,"%s"],' % (x[3:], x[len(x)-1:], pmap['6001'], pmap['6001'] / pmap['6007'], gmap['5006'], pmap['6002'],\
-                                                                             gmap['5002'],gmap['5004'],\
-                                                                             pmap['6005'],pmap['6006'],pmap['6008'],
-                                                                             
-                                                                             (gmap['5002'] - (pmap['6001'] / pmap['6007'])) / pmap['6001'] / pmap['6007'],
-                                                                             pmap['last_updated'])                
+                str = None
+                if not 'FUT' in x:
+                    pl_percent = (1 - gmap['5006'] / (pmap['6001'] / pmap['6007'])) * 100.0 if pmap['6002'] < 0 else  (gmap['5006'] - pmap['6001'] / pmap['6007']) / (pmap['6001'] / pmap['6007']) * 100  
+    
+    #                 s = '["%s","%s",%f,%f,%f,%f,%f,%f,%f,%f,"%s"],' % (x[3:], x[len(x)-1:], pmap['6001'], gmap['5006'], pmap['6002'],\
+    #                                                                              gmap['5002'],gmap['5004'],\
+    #                                                                              pmap['6005'],pmap['6006'],pmap['6008'],pmap['last_updated'])
+            # 2016/06/11        
+                    str = '["%s","%s",%f,%f,%f,%f,%f,%f,%f,%f,%f,%f],' % (x[3:], x[len(x)-1:], pmap['6001'], pmap['6001'] / pmap['6007'], gmap['5006'], pmap['6002'],\
+                                                                                 gmap['5002'],gmap['5004'],\
+                                                                                 pmap['6005'],pmap['6006'],pmap['6008'],
+                                                                                 pl_percent)
+                else:
+                    str = '["%s","%s",%f,%f,%f,%f,%f,%f,%f,%f,%f,%f],' % (x[3:], 'F', pmap['6001'], pmap['6001'] / pmap['6007'], 0, pmap['6002'],\
+                                                                                 0, 0,\
+                                                                                 pmap['6005'],0,-1,
+                                                                                 -1)
+                    
             except:
-                logging.error('entry %s skipped due to an exception. Please validate your position' % x)
+                logging.error('split_toks: entry %s skipped due to an exception. Please validate your position' % x)
                 return ''
-            return s                                                          
+            return str                                                         
             
         end_s = s + ''.join (split_toks( x ) for x in pall)
         return end_s 
@@ -394,7 +402,12 @@ class PortfolioManager():
         for l in sorted(self.port):
             content = []    
             toks= l.split(',')
- #           print toks
+            #
+            # lc - 2017
+            # skip futures entries 
+#             if toks[1] == 'FUT':
+#                 continue
+
             for i in s_cols:
                 content.append(toks[i])
             for i in i_cols:
@@ -417,17 +430,181 @@ class PortfolioManager():
         logging.debug('recal_port: acquiring lock...%s' % threading.currentThread())
         self.tlock.acquire()
         try:
-            s = self.recal_port_rentrant_unsafe()
+            #s = self.recal_port_rentrant_unsafe()
+            s = self.recal_port_rentrant_unsafe_ex()
         finally:
             logging.debug('recal_port: completed recal. releasing lock...')
             self.tlock.release()  
             
         return s
 
-    def recal_port_rentrant_unsafe(self):
+#     def recal_port_rentrant_unsafe(self):
+#         
+#         
+#         logging.debug('PortfolioManager: recal_port')
+# 
+#         # retrieve the portfolio entries from redis, strip the prefix in front
+#         plines = map(lambda k: (k.replace(self.rs_port_keys['port_prefix'] + '_', '')),\
+#                                  self.r_conn.keys(pattern=('%s*'% self.rs_port_keys['port_prefix'])))
+#                                  
+# 	# lc 2017
+# 	# temp fix to bypass errors when encountering futures contract
+# 	plines = filter(lambda k: not 'FUT' in k, plines)
+#         logging.info ("PortfolioManager: recal_port-> gathering position entries from redis %s" % plines)
+#         
+#         
+#         # 2017 added a new colum delta_f
+#         pos_summary = {'delta_c': 0.0, 'delta_p': 0.0, 'delta_all': 0.0, \
+#                        'theta_c': 0.0, 'theta_p': 0.0, 'theta_all': 0.0,\
+#                        'delta_1percent' : 0.0, 'theta_1percent' : 0.0,\
+#                        'iv_plus1p': 0.0, 'iv_minus1p': 0.0, 'unreal_pl': 0.0}
+#         l_gmap = []
+#         l_skipped_pos =[]       
+#         t_pos_multiplier = 0.0
+#         
+#         for ckey in plines:
+#         
+#             gmap = self.get_greeks(ckey)
+#             logging.debug('PortfolioManager: recal_port greeks market data %s->%s ' % (ckey, gmap))
+#             logging.debug('PortfolioManager: recal_port position-map->%s' % (self.r_get(ckey)))
+#             pmap = json.loads(self.r_get(ckey)) 
+#             
+#             
+#             
+#             if gmap:
+#             
+#             # Tick Value      Description
+#             # 5001            impl vol
+#             # 5002            delta
+#             # 5003            gamma
+#             # 5004            theta
+#             # 5005            vega
+#             # 5006            premium        
+#             # 6001            avgCost
+#             # 6002            pos
+#             # 6003            totCost
+#             # 6004            avgPx
+#             # 6005            pos delta
+#             # 6006            pos theta
+#             # 6007            multiplier
+#             # 6009            curr_port_value
+#             # 6008            unreal_pl
+#             # 6020            pos value impact +1% vol change
+#             # 6021            pos value impact -1% vol change
+#             
+#            
+#                 def pos_delta():                 
+#                     pd = pmap['6002'] * gmap['5002'] * pmap['6007']
+#                     logging.debug('PortfolioManager: recal_port: pos_delta: %f' % pd)
+#                     return pd
+#                 
+#                 def pos_theta():
+#                     pd = pmap['6002'] * gmap['5004'] * pmap['6007']
+#                     logging.debug('PortfolioManager: recal_port: pos_theta: %f' % pd)
+#                     return pd
+#  
+#                 def pos_avg_px():
+#                     pd = pmap['6001'] / pmap['6007']
+#                     logging.debug('PortfolioManager: recal_port: pos_avg_px: %f' % pd)
+#                     return pd                   
+#                     
+#                 def pos_tot_cost():
+#                     pd = pmap['6001'] * pmap['6002'] * pmap['6007']
+#                     logging.debug('PortfolioManager: recal_port: pos_tot_cost: %f' % pd)
+#                     return pd                   
+#                 
+#                 def pos_unreal_pl():
+#                     #(spot premium * multiplier - avgcost) * pos) 
+#                     v = (gmap['5006'] * pmap['6007'] - pmap['6001']) * pmap['6002'] 
+#                     return v 
+#     
+#                 
+#     
+#                 #logging.debug('PortfolioManager: recal_port greeks %f' % pos_delta(gmap))
+#                 
+#                 pmap['6005'] = pos_delta()
+#                 pmap['6006'] = pos_theta()
+#                 pmap['6004'] = pos_avg_px()
+#                 pmap['6003'] = pos_tot_cost()
+#                 pmap['6008'] = pos_unreal_pl()
+#                 pmap['last_updated'] = datetime.datetime.now().strftime('%Y%m%d%H%M%S')
+#                 
+#                 l_gmap.append(pmap)          
+# 
+#                 #t_pos_multiplier += (pmap['6002'] * pmap['6007'])
+# 
+#                 right = ckey.split('-')[3].lower()
+#                 pos_summary['delta_' + right] += pmap['6005']
+#                 pos_summary['delta_all'] += pmap['6005']
+#                 pos_summary['theta_' + right] += pmap['6006']
+#                 pos_summary['theta_all'] += pmap['6006']
+#                 pos_summary['unreal_pl'] += pmap['6008']
+# 
+#                 
+#                 
+#                 #pos_summary['delta_1percent'] += (pos_summary['delta_all'] / (pmap['6002'] * pmap['6007']))
+#                 # delta_1% = pos_ / (pos * multiplier)
+#                 
+#                 
+#                 #print 'con,right,avgcost,spot px,pos,delta,theta, pos_delta,pos_theta,pos_unreal_pl,last_updated'
+# #                 print ( 'PT_entries: %s,%s,%f,%f,%f,%f,%f,%f,%f,%f,%s' % (ckey, right, pmap['6001'], gmap['5006'], pmap['6002'],\
+# #                                                                      gmap['5002'],gmap['5004'],\
+# #                                                                      pmap['6005'],pmap['6006'],pmap['6008'],pmap['last_updated']
+# #                                                                      ))
+# #20150911                
+#                 #print pmap
+#                 #print json.dumps(pmap)
+#                 self.r_set(ckey, json.dumps(pmap))
+#                 
+#                 logging.debug('PortfolioManager: update position in redis %s' % self.r_get(ckey))
+#                 
+#             else:
+#                 l_skipped_pos.append(ckey)
+# 
+#             
+# #            self.r_set(ckey, json.dumps(pmap))
+# #            logging.debug('PortfolioManager: update position in redis %s' % self.r_get(ckey))
+# 
+#  
+#         #pos_summary['delta_1percent'] = (pos_summary['delta_all'] / t_pos_multiplier)
+#         
+#         
+# 
+#  
+#         if len(l_skipped_pos) > 0:
+#             logging.warn('***************** PortfolioManager: recal_port. SOME POSITIONS WERE NOT PROCESSED!')
+#             logging.warn('----------------- DO NOT rely on the numbers in the Summary dictionary (pos_summary)')
+#             logging.warn('----------------- Please check your portfolio through other means or subscribe missing')
+#             logging.warn('----------------- market data in options_serve.py console. ')
+#         logging.info('-------------- POSITION SUMMARY')
+#         pos_summary['last_updated'] = datetime.datetime.now().strftime('%Y%m%d%H%M%S')    
+#         pos_summary['entries_skipped'] = l_skipped_pos
+#         pos_summary['status'] = 'OK' if len(l_skipped_pos) == 0 else 'NOT_OK'
+#         #self.r_set(self.rs_port_keys['port_summary'], json.dumps(pos_summary) )
+#         t_pos_summary = json.dumps(pos_summary)
+#         self.r_conn.set(self.rs_port_keys['port_summary'], t_pos_summary )
+#         self.r_conn.set(self.rs_port_keys['port_items'], json.dumps(l_gmap))
+#         #print pos_summary
+#         #print l_gmap      
+#         # broadcast 
+#         if self.epc['epc']:
+#             try:
+#                 self.epc['epc'].post_portfolio_summary(pos_summary)
+#                 self.epc['epc'].post_portfolio_items(l_gmap)
+#             except:
+#                 logging.exception("Exception in function: recal_port_rentrant_unsafe")
+# 
+#         #logging.info(pos_summary)
+#         
+#         logging.warn('-------------- Entries for which the greeks are not computed!! %s' %\
+#                         ','.join(' %s' % k for k in l_skipped_pos))
+#         
+#         return l_gmap
+ 
+    def recal_port_rentrant_unsafe_ex(self):
         
         
-        logging.debug('PortfolioManager: recal_port')
+        logging.debug('PortfolioManager: recal_port_ex')
 
         # retrieve the portfolio entries from redis, strip the prefix in front
         plines = map(lambda k: (k.replace(self.rs_port_keys['port_prefix'] + '_', '')),\
@@ -435,7 +612,10 @@ class PortfolioManager():
                                  
         logging.info ("PortfolioManager: recal_port-> gathering position entries from redis %s" % plines)
         
-        pos_summary = {'delta_c': 0.0, 'delta_p': 0.0, 'delta_all': 0.0,\
+        
+        # 2017 lc
+        # add a new column: delta_f
+        pos_summary = {'delta_c': 0.0, 'delta_p': 0.0, 'delta_f': 0.0, 'delta_all': 0.0,\
                        'theta_c': 0.0, 'theta_p': 0.0, 'theta_all': 0.0,\
                        'delta_1percent' : 0.0, 'theta_1percent' : 0.0,\
                        'iv_plus1p': 0.0, 'iv_minus1p': 0.0, 'unreal_pl': 0.0}
@@ -446,15 +626,18 @@ class PortfolioManager():
         for ckey in plines:
         
             gmap = self.get_greeks(ckey)
-            logging.debug('PortfolioManager: recal_port greeks market data %s->%s ' % (ckey, gmap))
-            logging.debug('PortfolioManager: recal_port position-map->%s' % (self.r_get(ckey)))
-            pmap = json.loads(self.r_get(ckey)) 
             
+            print "*** ckey %s gmap %s" % (ckey,str(gmap))
             
-            
-            if gmap:
-            
-            # Tick Value      Description
+            # if gmap is not None, it implies it could be either an option with greek values in the redis map OR
+            # a futures contract (no option greeks but contains market data fields in the gmap)
+            # if gmap is None, it implies that the entry is either an option with no greek values OR
+            # a MHI contract 
+            # 
+            # why so complicate? the original design did not save MHI into the redis map
+            # the next if stmt tests for an entry and test whether it is a HSI, MHI, or an option with greeks map
+            if gmap or (not gmap and 'MHI' in ckey): 
+                # Tick Value      Description
             # 5001            impl vol
             # 5002            delta
             # 5003            gamma
@@ -472,33 +655,63 @@ class PortfolioManager():
             # 6008            unreal_pl
             # 6020            pos value impact +1% vol change
             # 6021            pos value impact -1% vol change
-            
+                def is_futures(ckey):
+                    return 'FUT' in ckey
            
-                def pos_delta():                 
-                    pd = pmap['6002'] * gmap['5002'] * pmap['6007']
+                def pos_delta():
+                    delta = 0.0     
+                    if not is_futures(ckey):
+                        
+                        delta = gmap['5002']
+                    else:
+                        delta = 1.0
+                          
+                    print "***** delta chosen %f" % delta         
+                    pd = pmap['6002'] * delta * pmap['6007']
                     logging.debug('PortfolioManager: recal_port: pos_delta: %f' % pd)
                     return pd
                 
                 def pos_theta():
-                    pd = pmap['6002'] * gmap['5004'] * pmap['6007']
+                    pd = pmap['6002'] * gmap['5004'] * pmap['6007'] if not is_futures(ckey) else 0
                     logging.debug('PortfolioManager: recal_port: pos_theta: %f' % pd)
                     return pd
  
                 def pos_avg_px():
-                    pd = pmap['6001'] / pmap['6007']
+                    pd = pmap['6001'] / pmap['6007'] # if not is_futures(ckey) else pmap['6001'] 
                     logging.debug('PortfolioManager: recal_port: pos_avg_px: %f' % pd)
                     return pd                   
                     
                 def pos_tot_cost():
-                    pd = pmap['6001'] * pmap['6002'] * pmap['6007']
+                    pd = pmap['6001'] * pmap['6002'] * pmap['6007'] if not is_futures(ckey) else pmap['6001'] * pmap['6002'] 
                     logging.debug('PortfolioManager: recal_port: pos_tot_cost: %f' % pd)
                     return pd                   
                 
                 def pos_unreal_pl():
                     #(spot premium * multiplier - avgcost) * pos) 
-                    v = (gmap['5006'] * pmap['6007'] - pmap['6001']) * pmap['6002'] 
+                    if not is_futures(ckey):
+                        v = (gmap['5006'] * pmap['6007'] - pmap['6001']) * pmap['6002']
+                    else: 
+                        try:
+                            v = 0
+                            nkey = ckey.replace('MHI', 'HSI')
+                            data = self.r_conn.get(nkey)
+                            
+                            if data != None:
+                                #
+                                # (S - X) * pos * multiplier
+                                
+                                spot = json.loads(data)['4']
+                                v = (spot - pmap['6001']) * pmap['6007'] * pmap['6002']                         
+                        except:
+                            # there could be no data before market open
+                            # the look up for last price could have failed. 
+                            logging.error("PortfolioManager:pos_unreal_pl error: unable to calculate futures P/L!")
+                            
                     return v 
     
+                logging.debug('PortfolioManager: recal_port greeks market data %s->%s ' % (ckey, gmap))
+                logging.debug('PortfolioManager: recal_port position-map->%s' % (self.r_get(ckey)))
+                pmap = json.loads(self.r_get(ckey)) 
                 
     
                 #logging.debug('PortfolioManager: recal_port greeks %f' % pos_delta(gmap))
@@ -514,19 +727,18 @@ class PortfolioManager():
 
                 #t_pos_multiplier += (pmap['6002'] * pmap['6007'])
 
-                right = ckey.split('-')[3].lower()
-                pos_summary['delta_' + right] += pmap['6005']
-                pos_summary['delta_all'] += pmap['6005']
-                pos_summary['theta_' + right] += pmap['6006']
-                pos_summary['theta_all'] += pmap['6006']
-                pos_summary['unreal_pl'] += pmap['6008']
+                if not is_futures(ckey):
+                    right = ckey.split('-')[3].lower()
+                    pos_summary['delta_' + right] += pmap['6005']
+                    pos_summary['delta_all'] += pmap['6005']
+                    pos_summary['theta_' + right] += pmap['6006']
+                    pos_summary['theta_all'] += pmap['6006']
+                    pos_summary['unreal_pl'] += pmap['6008']
+                else:
+                    pos_summary['delta_f'] += pmap['6005']
+                    pos_summary['delta_all'] += pmap['6005']
+                    print "**** adding delta %f cum_delta_f %f cum_delta_all %f" % (pmap['6005'], pos_summary['delta_f'], pos_summary['delta_all'])
 
-                
-                
-                #pos_summary['delta_1percent'] += (pos_summary['delta_all'] / (pmap['6002'] * pmap['6007']))
-                # delta_1% = pos_ / (pos * multiplier)
-                
-                
                 #print 'con,right,avgcost,spot px,pos,delta,theta, pos_delta,pos_theta,pos_unreal_pl,last_updated'
 #                 print ( 'PT_entries: %s,%s,%f,%f,%f,%f,%f,%f,%f,%f,%s' % (ckey, right, pmap['6001'], gmap['5006'], pmap['6002'],\
 #                                                                      gmap['5002'],gmap['5004'],\
@@ -538,9 +750,13 @@ class PortfolioManager():
                 self.r_set(ckey, json.dumps(pmap))
                 
                 logging.debug('PortfolioManager: update position in redis %s' % self.r_get(ckey))
+ 
+            else: # greeks entry not found
                 
-            else:
                 l_skipped_pos.append(ckey)
+            
+           
+                
 
             
 #            self.r_set(ckey, json.dumps(pmap))
@@ -562,7 +778,10 @@ class PortfolioManager():
         pos_summary['entries_skipped'] = l_skipped_pos
         pos_summary['status'] = 'OK' if len(l_skipped_pos) == 0 else 'NOT_OK'
         #self.r_set(self.rs_port_keys['port_summary'], json.dumps(pos_summary) )
+        
         t_pos_summary = json.dumps(pos_summary)
+        
+        print "****** %s" % t_pos_summary
         self.r_conn.set(self.rs_port_keys['port_summary'], t_pos_summary )
         self.r_conn.set(self.rs_port_keys['port_items'], json.dumps(l_gmap))
         #print pos_summary
@@ -581,7 +800,7 @@ class PortfolioManager():
                         ','.join(' %s' % k for k in l_skipped_pos))
         
         return l_gmap
- 
+
 
     def construct_port(self, pos_msg):
         # port structure
@@ -668,9 +887,11 @@ if __name__ == '__main__':
     p.retrieve_position()
     print p.get_portfolio_summary()
     print p.get_tbl_pos_csv()
-    p.group_pos_by_strike_by_month()
-    print p.grouped_options
-    print p.get_grouped_options_str_array_stacked()
+    print "***********************"
+    print p.get_tbl_pos_list()
+#     p.group_pos_by_strike_by_month()
+#     print p.grouped_options
+#     print p.get_grouped_options_str_array_stacked()
 
     # sample ouput    
 # ["exch","type","contract_mth","right","strike","con_ration","pos","avgcost"],["HSI","OPT","20150828","C","22600",50.0,0.0000,0.0000,],["HSI","OPT","20150828","C","23000",50.0,-1.0000,1770.0000,],["HSI","OPT","20150828","C","23600",50.0,-2.0000,1470.0000,],["HSI","OPT","20150828","C","23800",50.0,-1.0000,920.0000,],["HSI","OPT","20150828","C","24000",50.0,-2.0000,1820.0000,],["HSI","OPT","20150828","C","24200",50.0,-1.0000,3120.0000,],["HSI","OPT","20150828","C","24800",50.0,-1.0000,220.0000,],["HSI","OPT","20150828","P","18000",50.0,-2.0000,1045.0000,],["HSI","OPT","20150828","P","18600",50.0,-1.0000,1120.0000,],["HSI","OPT","20150828","P","18800",50.0,-1.0000,1570.0000,],["HSI","OPT","20150828","P","19800",50.0,-1.0000,870.0000,],["HSI","OPT","20150828","P","20200",50.0,-1.0000,970.0000,],["HSI","OPT","20150828","P","20800",50.0,-2.0000,970.0000,],["HSI","OPT","20150828","P","21600",50.0,-1.0000,1570.0000,],["HSI","OPT","20150828","P","21800",50.0,-7.0000,1955.7143,],["HSI","OPT","20150828","P","23200",50.0,1.0000,25930.0000,],["HSI","OPT","20150929","C","24400",50.0,1.0000,24880.0000,],["HSI","OPT","20150929","P","21600",50.0,0.0000,0.0000,],["HSI","OPT","20150929","P","21800",50.0,2.0000,52713.3333,],["HSI","OPT","20150929","P","22600",50.0,3.0000,39763.3333,],["MHI","OPT","20150828","C","24400",10.0,-1.0000,2603.0000,],["MHI","OPT","20150828","P","20800",10.0,-1.0000,313.0000,],["MHI","OPT","20150828","P","21000",10.0,-1.0000,363.0000,],["MHI","OPT","20150828","P","23600",10.0,5.0000,4285.0000,],["MHI","OPT","20150929","C","24400",10.0,1.0000,4947.0000,],["MHI","OPT","20150929","P","21600",10.0,1.0000,12657.0000,],["MHI","OPT","20150929","P","22600",10.0,1.0000,9877.0000,],["MHI","OPT","20150929","P","23600",10.0,4.0000,7757.0000,],

+ 214 - 0
html/opt-pos-chart-stacked-tmpl-2017bak.html

@@ -0,0 +1,214 @@
+<html>
+  <head>
+  <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
+    <script type="text/javascript" src="https://www.google.com/jsapi"></script>
+    <script type="text/javascript">
+      google.load("visualization", "1.1", {packages:["corechart", 'table','gauge']});
+//      google.load('visualization', '1', {'packages':['table']});
+//      google.load("visualization", "1.1", {packages:["corechart"]});
+      google.setOnLoadCallback(drawChart);
+      function drawChart() {
+        var data = google.visualization.arrayToDataTable([
+          ['strike', 'Near-P', 'Near-C', 'Far-P', 'Far-C'],
+{{{dataPCpos}}}
+        ]);
+
+	var dataTbl = google.visualization.arrayToDataTable([
+{{{dataTablePos}}}
+
+        ]);
+
+
+        var options = {
+	  title: 'PUT:CALL Ratio [{{{PRvsCR}}}}] for the month of {{{option_months}}}',
+          width: 900,
+ 	  legend:{ position: 'top', maxLines: 2},
+	  bar:{groupWidth: '20%'},
+	  chartArea:{left:40,top:50,width:"80%",height:"70%"},
+ 	  isStacked: true,
+	  series:{
+		0: {color:'#0066ff'},		  
+		1: {color:'#e60000'},
+		2: {color:'#80b3ff'},
+		3: {color:'#ff8080'},
+
+		
+	  },
+        };
+
+        
+        var chart = new google.visualization.ColumnChart(document.getElementById('barchart_material'));
+        chart.draw(data, options);
+
+
+
+
+	// chart table display implied volatilities in a nicely formatted table	
+       var chartTbl = new google.visualization.Table(document.getElementById('chartTbl_div'));
+
+
+       //var portTbl = new google.visualization.Table(document.getElementById('portTblDiv'));
+
+	var formatterColor = new google.visualization.ColorFormat();
+	formatterColor.addRange(-100, 0, 'white', 'red');
+	formatterColor.addRange(0, 100, 'white', 'blue');
+	formatterColor.format(dataTbl, 5); 
+
+	var formatterColor2 = new google.visualization.ColorFormat();
+	formatterColor2.addGradientRange(null, null, 'white', 'orange', 'blue');
+	formatterColor2.format(dataTbl, 10); 
+
+	var formatter = new google.visualization.NumberFormat({pattern:'0.00'});
+	for (var i = 2; i < 12; i++){
+		formatter.format(dataTbl, i); 
+	}
+        var options2 = {
+          displayAnnotations: true,
+	  showRowNumber: true, width: '100%', height: '100%', allowHtml: true,
+        };
+        var formatterBar = new google.visualization.BarFormat({width: 80,
+				colorPositive: 'green', max:100 });       
+        formatterBar.format(dataTbl, 11);
+	chartTbl.draw(dataTbl, options2);
+
+	$('input[id="b_reload"]').click();
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// guage functions
+
+		var dataG = google.visualization.arrayToDataTable([
+		  ['Label', 'Value'],
+		  ['Delta', 0],
+		  ['Delta-F', 0],
+		  ['Delta-C', 0],
+		  ['Delta-P', 0]
+		]);
+
+		var optionsG = {
+
+		  width: 400, height: 120,
+		  redFrom: -50, redTo: -25,
+		  yellowFrom:-24, yellowTo: -15,
+		  greenFrom: -10, greenTo: 10,	
+		  minorTicks: 5,
+		  min: -50, max: 50
+		};
+
+		var dataT = google.visualization.arrayToDataTable([
+		  ['Label', 'Value'],
+		  ['Theta', 0],
+		  ['Theta-C', 0],
+		  ['Theta-P', 0]
+		]);
+
+		var optionsT = {
+
+		  width: 400, height: 120,
+		  redFrom: -3000, redTo: -2000,
+		  yellowFrom:-1999, yellowTo: 500,
+		  greenFrom: 5000, greenTo: 15000,	
+		  minorTicks: 5,
+		  min: -3000, max: 15000
+		};
+
+		var gchart = new google.visualization.Gauge(document.getElementById('chart_div'));
+//		refreshPortSummary();
+//		gchart.draw(dataG, optionsG);
+
+		var tchart = new google.visualization.Gauge(document.getElementById('chartTheta_div'));
+		refreshPortSummary();
+		
+		function refreshPortSummary(){
+		    $.ajax({
+			type: 'Post',
+			url: '/ws_port_summary',
+			success: function (data) {
+				//alert(data);
+				var jdata = JSON.parse(data);
+				$('#temp').text(data);				
+				//alert(jdata.delta_all);
+				
+				dataG.setValue(0, 1, Math.round(jdata.delta_all*100)/100);
+				dataG.setValue(1, 1, Math.round(jdata.delta_f*100)/100);
+				dataG.setValue(2, 1, Math.round(jdata.delta_c*100)/100);
+				dataG.setValue(3, 1, Math.round(jdata.delta_p*100)/100);
+				gchart.draw(dataG, optionsG);
+
+				dataT.setValue(0, 1, Math.round(jdata.theta_all));
+				dataT.setValue(1, 1, Math.round(jdata.theta_c));
+				dataT.setValue(2, 1, Math.round(jdata.theta_p));
+				tchart.draw(dataT, optionsT);
+
+				$('#pt_status').text("Unreal P/L: " + Math.round(jdata.unreal_pl*100)/100 + "   Cal Status: " + jdata.status);
+
+			}
+		    });
+
+		};
+
+		function isMarketOpen(now){
+			var hkex_mstart_hhmm = '0915';
+			var hkex_mend_hhmm = '1200';
+			var hkex_astart_hhmm = '1300';
+			var hkex_aend_hhmm = '1630';
+
+			var now_hhmm = ('0' + now.getHours()).slice(-2) + '' + ('0' + now.getMinutes()).slice(-2);
+			rc = ( 
+				(hkex_mstart_hhmm < now_hhmm && now_hhmm < hkex_mend_hhmm) || 
+				(hkex_astart_hhmm < now_hhmm && now_hhmm < hkex_aend_hhmm)
+		       	) ? true : false;
+			return rc;
+		}
+
+		setInterval(function() {
+			//var rc = isMarketOpen(new Date());
+			if (isMarketOpen(new Date())){
+			//if (rc){
+				refreshPortSummary();
+			}
+		}, 9500);    
+
+
+
+
+
+
+
+
+      } // end drawChart
+
+
+
+
+
+
+
+	
+
+
+		//})
+
+//	});
+
+    </script>
+
+
+  </head>
+  <body>
+<div id='pt_status'></div>
+<div id="chart_div" style="width: 400px; height: 120px;"></div>
+<div id="chartTheta_div" style="width: 400px; height: 120px;"></div>
+
+    <div id="barchart_material" style="width: 900px; height: 500px;"></div>
+    <div id='chartTbl_div' style='width: 900px; height: 500px;'></div>
+    <div id="portTbl">
+        <input type="button" id="b_reload" value="Reload" />
+    </div>
+    <div id='portTblDiv' style='width: 900px; height: 500px;'></div>
+<div id='temp'></div>
+
+  </body>
+</html>
+

+ 38 - 5
html/opt-pos-chart-stacked-tmpl.html

@@ -9,7 +9,7 @@
       google.setOnLoadCallback(drawChart);
       function drawChart() {
         var data = google.visualization.arrayToDataTable([
-          ['strike', 'Near-P', 'Near-C', 'Far-P', 'Far-C'],
+           ['strike', 'Near-P', 'Near-C', 'Far-P', 'Far-C','Near-F', 'Far-F'],
 {{{dataPCpos}}}
         ]);
 
@@ -49,13 +49,26 @@
 
        //var portTbl = new google.visualization.Table(document.getElementById('portTblDiv'));
 
+	var formatterColor = new google.visualization.ColorFormat();
+	formatterColor.addRange(-100, 0, 'white', 'red');
+	formatterColor.addRange(0, 100, 'white', 'blue');
+	formatterColor.format(dataTbl, 5); 
+
+	var formatterColor2 = new google.visualization.ColorFormat();
+	formatterColor2.addGradientRange(null, null, 'white', 'orange', 'blue');
+	formatterColor2.format(dataTbl, 10); 
+
 	var formatter = new google.visualization.NumberFormat({pattern:'0.00'});
-	for (var i = 2; i < 9; i++)
+	for (var i = 2; i < 12; i++){
 		formatter.format(dataTbl, i); 
+	}
         var options2 = {
           displayAnnotations: true,
 	  showRowNumber: true, width: '100%', height: '100%', allowHtml: true,
         };
+        var formatterBar = new google.visualization.BarFormat({width: 80,
+				colorPositive: 'green', max:100 });       
+        formatterBar.format(dataTbl, 11);
 	chartTbl.draw(dataTbl, options2);
 
 	$('input[id="b_reload"]').click();
@@ -68,6 +81,7 @@
 		var dataG = google.visualization.arrayToDataTable([
 		  ['Label', 'Value'],
 		  ['Delta', 0],
+		  ['Delta-F', 0],
 		  ['Delta-C', 0],
 		  ['Delta-P', 0]
 		]);
@@ -117,8 +131,9 @@
 				//alert(jdata.delta_all);
 				
 				dataG.setValue(0, 1, Math.round(jdata.delta_all*100)/100);
-				dataG.setValue(1, 1, Math.round(jdata.delta_c*100)/100);
-				dataG.setValue(2, 1, Math.round(jdata.delta_p*100)/100);
+				dataG.setValue(1, 1, Math.round(jdata.delta_f*100)/100);
+				dataG.setValue(2, 1, Math.round(jdata.delta_c*100)/100);
+				dataG.setValue(3, 1, Math.round(jdata.delta_p*100)/100);
 				gchart.draw(dataG, optionsG);
 
 				dataT.setValue(0, 1, Math.round(jdata.theta_all));
@@ -133,8 +148,26 @@
 
 		};
 
+		function isMarketOpen(now){
+			var hkex_mstart_hhmm = '0915';
+			var hkex_mend_hhmm = '1200';
+			var hkex_astart_hhmm = '1300';
+			var hkex_aend_hhmm = '1630';
+
+			var now_hhmm = ('0' + now.getHours()).slice(-2) + '' + ('0' + now.getMinutes()).slice(-2);
+			rc = ( 
+				(hkex_mstart_hhmm < now_hhmm && now_hhmm < hkex_mend_hhmm) || 
+				(hkex_astart_hhmm < now_hhmm && now_hhmm < hkex_aend_hhmm)
+		       	) ? true : false;
+			return rc;
+		}
+
 		setInterval(function() {
-			refreshPortSummary();
+			//var rc = isMarketOpen(new Date());
+			if (isMarketOpen(new Date())){
+			//if (rc){
+				refreshPortSummary();
+			}
 		}, 9500);    
 
 

+ 0 - 1
html/opt-pos-chart-tmpl.html

@@ -123,7 +123,6 @@
 		    });
 
 		};
-
 		setInterval(function() {
 			refreshPortSummary();
 		}, 9500);    

+ 2 - 2
misc2/helpers.py

@@ -122,8 +122,8 @@ class ContractHelper(BaseHelper):
     def makeRedisKey(contract):
         #print "makerediskey %s" % ContractHelper.printContract(contract)
 #20150904        
-        #contract.m_strike = int(contract.m_strike)
-        contract.m_strike = contract.m_strike
+        contract.m_strike = int(contract.m_strike)
+        #contract.m_strike = contract.m_strike
         
         if contract.m_secType == 'OPT':
             s = '%s-%s-%s-%s' % (contract.m_symbol,

+ 5 - 0
sh/kill_opt_serve.sh

@@ -0,0 +1,5 @@
+#!/bin/bash
+ROOT=~/production/finopt
+export PYTHONPATH=$ROOT
+
+kill -9  $(cat $ROOT/opt_serve.pid)

+ 5 - 0
sh/kill_options_data.sh

@@ -0,0 +1,5 @@
+#!/bin/bash
+ROOT=~/production/finopt
+export PYTHONPATH=$ROOT
+
+kill -9  $(cat $ROOT/options_data.pid)

+ 2 - 1
sh/portfolio.sh

@@ -1,4 +1,5 @@
 #!/bin/bash
-ROOT=/home/larry/l1304/workspace/finopt/src
+ROOT=/home/itchyape/production/finopt
 export PYTHONPATH=$ROOT
 python $ROOT/finopt/portfolio.py $ROOT/config/app.cfg
+#python $ROOT/finopt/portfolio-2017.py $ROOT/config/app.cfg