Răsfoiți Sursa

working alpha - port monitor

bobhk 9 ani în urmă
părinte
comite
f724fca165

+ 2 - 1
src/finopt/optcal.py

@@ -21,12 +21,13 @@ def cal_implvol(spot, strike, callput, evaldate, exdate, rate, div, vol, premium
     r = YieldTermStructureHandle(FlatForward(str2qdate(evaldate), rate, Actual365Fixed()))
     q = YieldTermStructureHandle(FlatForward(str2qdate(evaldate), div, Actual365Fixed()))
 
+
     sigma = BlackVolTermStructureHandle(BlackConstantVol(str2qdate(evaldate), HongKong(), vol, Actual365Fixed()))
     process = BlackScholesMertonProcess(S,q,r,sigma)
     im = option.impliedVolatility(premium, process)
+
     results = {}
     results[instrument.Option.IMPL_VOL] = im
- 
     
     return results
 

+ 6 - 3
src/rethink/option_chain.py

@@ -104,12 +104,13 @@ class OptionsChain(Publisher):
     def set_trade_vol(self, tvol):
         self.trade_vol = tvol
         
-    def set_option_structure(self, underlying, spd_size, multiplier, rate, div, expiry):
+    def set_option_structure(self, underlying, spd_size, multiplier, rate, div, expiry, trade_vol=0.15):
         self.set_div(div)
         self.set_rate(rate)
         self.set_spread_table(spd_size, multiplier)
         self.set_underlying(underlying)
         self.set_expiry(expiry)
+        self.set_trade_vol(trade_vol)
       
     def build_chain(self, px, bound, trade_vol):
         self.set_trade_vol(trade_vol)
@@ -232,10 +233,12 @@ class OptionsChain(Publisher):
             
             
         try:
-            logging.info('OptionChain:cal_option_greeks. ulspot->%8.4f, premium last->%8.4f ' % (uspot_last, option.get_tick_value(4)))
+            #logging.info('OptionChain:cal_option_greeks. ulspot->%8.4f, premium last->%8.4f ' % (uspot_last, option.get_tick_value(4)))
+            #logging.info('OptionChain:cal_option_greeks. o.m_strike %8.4f, o.m_right %s, valuation_date %s, o.m_expiry %s, self.rate %8.4f , self.div  %8.4f, self.trade_vol %8.4f ' % 
+            #            (o.m_strike, o.m_right, valuation_date,  o.m_expiry, self.rate, self.div, self.trade_vol))
             iv = cal_implvol(uspot_last, o.m_strike, o.m_right, valuation_date, 
                                   o.m_expiry, self.rate, self.div, self.trade_vol, option.get_tick_value(4))
-            logging.info('OptionChain:cal_option_greeks. cal results:iv=> %8.4f' % iv)
+            #logging.info('OptionChain:cal_option_greeks. cal results:iv=> %s' % str(iv))
         except RuntimeError:
             logging.warn('OptionChain:cal_option_greeks. Quantlib threw an error while calculating implied vol: use intrinsic: last->%8.2f strike->%8.2f right->%s sym->%s' % 
                          (uspot_last, o.m_strike, o.m_right, o.m_symbol))

+ 118 - 34
src/rethink/portfolio_monitor.py

@@ -1,3 +1,5 @@
+# -*- coding: utf-8 -*-
+import sys, traceback
 import logging
 import json
 import time, datetime
@@ -17,11 +19,12 @@ class PortfolioRules():
                 'symbol': {'HSI' : 'FUT', 'MHI' : 'FUT', 'QQQ' : 'STK'},
                 'expiry': {'HSI' : 'same_month', 'MHI': 'same_month', 'STK': 'leave_blank'},
                 'option_structure': {
-                                        'HSI': {'spd_size': 200, 'multiplier': 50, 'rate': 0.0012, 'div': 0},
-                                        'MHI': {'spd_size': 200, 'multiplier': 10, 'rate': 0.0012, 'div': 0}
+                                        'HSI': {'spd_size': 200, 'multiplier': 50, 'rate': 0.0012, 'div': 0, 'trade_vol':0.15},
+                                        'MHI': {'spd_size': 200, 'multiplier': 10, 'rate': 0.0012, 'div': 0, 'trade_vol':0.15}
                                         
                                     },
-                'exchange': {'HSI': 'HKFE', 'MHI': 'HKFE'}
+                'exchange': {'HSI': 'HKFE', 'MHI': 'HKFE'},
+                
                 
                } 
     
@@ -34,22 +37,29 @@ class PortfolioItem():
         average cost
     
     """
-    POSITION = 6001
-    AVERAGE_COST = 6002
-    POSITION_DELTA = 6003
-    POSITION_THETA = 6004
-    UNREAL_PL = 6005
-    PERCENT_GAIN_LOSS = 6006
-    AVERAGE_PRICE = 6007
-    ACCOUNT_ID = 6008
+    POSITION = 7001
+    AVERAGE_COST = 7002
+    POSITION_DELTA = 7003
+    POSITION_THETA = 7004
+    UNREAL_PL = 7005
+    PERCENT_GAIN_LOSS = 7006
+    AVERAGE_PRICE = 7007
+    #ACCOUNT_ID = 6008
     
         
     def __init__(self, account, contract_key, position, average_cost):
         
         self.contract_key = contract_key
-        self.quantity = position
-        self.average_cost = average_cost
         self.account_id = account
+        self.port_fields = {PortfolioItem.POSITION: position,
+                            PortfolioItem.AVERAGE_COST: average_cost,
+                            PortfolioItem.POSITION_DELTA: float('nan'),
+                            PortfolioItem.POSITION_THETA: float('nan'),
+                            PortfolioItem.UNREAL_PL: float('nan'),
+                            PortfolioItem.PERCENT_GAIN_LOSS: float('nan'),
+                            PortfolioItem.AVERAGE_PRICE: float('nan')
+                            
+                            }
         
         contract = ContractHelper.makeContractfromRedisKeyEx(contract_key)
         
@@ -59,6 +69,30 @@ class PortfolioItem():
         else: 
             self.instrument = Symbol(contract)
         
+
+    
+    def set_port_field(self, id, value):
+        self.port_fields[id] = value
+
+    def get_port_field(self, id):
+        try:
+            
+            return self.port_fields[id]
+    
+        except:
+            
+            return None    
+    def get_port_fields(self):
+        return self.port_fields
+    
+    def get_symbol_id(self):
+        return self.instrument.get_contract().m_symbol
+    
+    def get_quantity(self):
+        return self.port_fields[PortfolioItem.POSITION]
+    
+    def get_average_cost(self):
+        return self.port_fields[PortfolioItem.AVERAGE_COST]
     
     def get_instrument(self):
         return self.instrument
@@ -69,15 +103,57 @@ class PortfolioItem():
     def get_account(self):
         return self.account_id
         
-    def calculate_pl(self):
-        logging.info('PortfolioMonitor:calculate_pl. qty=%d avgcost=%8.4f' % (self.quantity, self.average_cost))
+    def calculate_pl(self, contract_key):
+        logging.info('PortfolioMonitor:calculate_pl. %s' % self.dump())
+        
+            
+    
+        try:
+            assert contract_key == self.contract_key
+#             POSITION = 6001
+#             AVERAGE_COST = 6002
+#             POSITION_DELTA = 6003
+#             POSITION_THETA = 6004
+#             UNREAL_PL = 6005
+#             PERCENT_GAIN_LOSS = 6006
+#             AVERAGE_PRICE = 6007            
+            if self.get_instrument_type() == 'OPT':
+                
+                pos_delta = self.get_quantity() * self.instrument.get_tick_value(Option.DELTA) * \
+                               PortfolioRules.rule_map['option_structure'][self.get_symbol_id()]['multiplier'] 
+                pos_theta = self.get_quantity() * self.instrument.get_tick_value(Option.THETA) * \
+                               PortfolioRules.rule_map['option_structure'][self.get_symbol_id()]['multiplier']
+
+                #(spot premium * multiplier - avgcost) * pos)
+                unreal_pl = (self.instrument.get_tick_value(4) * \
+                            PortfolioRules.rule_map['option_structure'][self.get_symbol_id()]['multiplier'] - \
+                            self.get_average_cost()) * self.get_quantity()
+            else:
+                pos_delta = self.get_quantity() * 1.0 * \
+                               PortfolioRules.rule_map['option_structure'][self.get_symbol_id()]['multiplier'] 
+                pos_theta = 0
+                # (S - X) * pos * multiplier
+                unreal_pl = (self.instrument.get_tick_value(4) - self.get_average_cost() ) * self.get_quantity() * \
+                               PortfolioRules.rule_map['option_structure'][self.get_symbol_id()]['multiplier']
+                        
+            self.set_port_field(PortfolioItem.POSITION_DELTA, pos_delta)
+            self.set_port_field(PortfolioItem.POSITION_THETA, pos_theta)
+            self.set_port_field(PortfolioItem.UNREAL_PL, unreal_pl)
+            
+        except Exception, err:
+            
+            logging.error(traceback.format_exc())     
+
+                        
+        logging.info('PortfolioMonitor:calculate_pl. %s' % self.dump())
     
     def update_position(self, position, average_cost):
-        self.quantity = position
-        self.average_cost = average_cost   
+        self.set_port_field(PortfolioItem.POSITION, position)
+        self.set_port_field(PortfolioItem.AVERAGE_COST, average_cost)
         
     def dump(self):
-        return 'PortfolioItem contents: %s %s %s' % (self.contract_key, self.quantity, self.average_cost)
+        s= ", ".join('[%s:%8.2f]' % (k, v) for k,v in self.port_fields.iteritems())
+        return 'PortfolioItem contents: %s %s %s' % (self.contract_key, self.account_id, s)
 
 
 class PortfolioMonitor(AbstractGatewayListener):
@@ -249,7 +325,8 @@ class PortfolioMonitor(AbstractGatewayListener):
                                     PortfolioRules.rule_map['option_structure'][underlying_id]['multiplier'],
                                     PortfolioRules.rule_map['option_structure'][underlying_id]['rate'],
                                     PortfolioRules.rule_map['option_structure'][underlying_id]['div'],
-                                    month)
+                                    month,
+                                    PortfolioRules.rule_map['option_structure'][underlying_id]['trade_vol'])
             
             self.portfolios[account]['opt_chains'][oc_id] = oc 
             
@@ -305,9 +382,8 @@ class PortfolioMonitor(AbstractGatewayListener):
         #<account_id>: {'port_items': {<contract_key>, instrument}, 'opt_chains': {<oc_id>: option_chain}}
         
         def print_port_items(x):
-            return '[%s]: %4d %8.2f %s' % (x[0], x[1].quantity, 
-                                           x[1].average_cost, 
-                                           ' '.join('%s:%s' % (k,v) for k, v in x[1].get_instrument().get_tick_values().iteritems()))
+            return '[%s]: %s %s' % (x[0],  ', '.join('%s: %s' % (k,str(v)) for k, v in x[1].get_port_fields().iteritems()),
+                                           ', '.join('%s: %s' % (k,str(v)) for k, v in x[1].get_instrument().get_tick_values().iteritems()))
         
         p_items = map(print_port_items, [x for x in self.portfolios[account]['port_items'].iteritems()])
         logging.info('PortfolioMonitor:dump_portfolio %s' % ('\n'.join(p_items)))
@@ -349,32 +425,41 @@ class PortfolioMonitor(AbstractGatewayListener):
             if OptionsChain.CHAIN_IDENTIFIER in s.get_extra_attributes():
                 results = {}
                 chain_id = s.get_extra_attributes()[OptionsChain.CHAIN_IDENTIFIER]
-                logging.info('PortfolioMonitor:tds_event_tick_updated chain_id %s' % chain_id)
+                #logging.info('PortfolioMonitor:tds_event_tick_updated chain_id %s' % chain_id)
                 
                 for acct in self.portfolios:
                     
                     if chain_id  in self.portfolios[acct]['opt_chains'].keys():
-                        logging.info('PortfolioMonitor:tds_event_tick_updated --> portfolio opt_chains: [  %s  ] ' % 
-                                     str(self.portfolios[acct]['opt_chains'].keys()))
+                        #logging.info('PortfolioMonitor:tds_event_tick_updated --> portfolio opt_chains: [  %s  ] ' % 
+                        #             str(self.portfolios[acct]['opt_chains'].keys()))
                         if 'FUT' in contract_key or 'STK' in contract_key:
                             results = self.portfolios[acct]['opt_chains'][chain_id].cal_greeks_in_chain(self.kwargs['evaluation_date'])
                         else:
                             results[ContractHelper.makeRedisKeyEx(s.get_contract())] =  self.portfolios[acct]['opt_chains'][chain_id].cal_option_greeks(s, self.kwargs['evaluation_date'])
-                    logging.info('PortfolioMonitor:tds_event_tick_updated. compute greek results %s' % results)    
-                    # set_analytics(self, imvol=None, delta=None, gamma=None, theta=None, vega=None, npv=None):
-                    # 
-                    def update_tds_analytics(key_greeks):
+                    #logging.info('PortfolioMonitor:tds_event_tick_updated. compute greek results %s' % results)
+                        
+                        #underlying_px = self.portfolios[acct]['opt_chains'][chain_id].get_underlying().get_tick_value(4)
+                        
+                    def update_portfolio_fields(key_greeks):
                         
                         self.tds.set_symbol_analytics(key_greeks[0], Option.IMPL_VOL, key_greeks[1][Option.IMPL_VOL])
                         self.tds.set_symbol_analytics(key_greeks[0], Option.DELTA, key_greeks[1][Option.DELTA])
                         self.tds.set_symbol_analytics(key_greeks[0], Option.GAMMA, key_greeks[1][Option.GAMMA])
-                        self.tds.set_2symbol_analytics(key_greeks[0], Option.THETA, key_greeks[1][Option.THETA])
+                        self.tds.set_symbol_analytics(key_greeks[0], Option.THETA, key_greeks[1][Option.THETA])
                         self.tds.set_symbol_analytics(key_greeks[0], Option.VEGA, key_greeks[1][Option.VEGA])
                         
-                    map(update_tds_analytics, list(results.iteritems()))                
-
+                        if contract_key in self.portfolios[acct]['port_items']:
+                            self.portfolios[acct]['port_items'][contract_key].calculate_pl(key_greeks[0]) #, underlying_px)
+                        
+                    if results:
+                        #logging.info('PortfolioMonitor:tds_event_tick_updated ....before map')
+                        map(update_portfolio_fields, list(results.iteritems()))
+                        #logging.info('PortfolioMonitor:tds_event_tick_updated ....after map')
+                           
+                               
+                    
             else:
-                
+                logging.info('PortfolioMonitor:tds_event_tick_updated ignoring uninterested ticks %s' % contract_key)
                 continue
              
         
@@ -388,7 +473,6 @@ class PortfolioMonitor(AbstractGatewayListener):
     #
 
     def tickPrice(self, event, contract_key, field, price, canAutoExecute):
-        logging.info('MessageListener:%s. %s %d %8.2f' % (event, contract_key, field, price))
         self.tds.set_symbol_tick_price(contract_key, field, price, canAutoExecute)
 
 

+ 5 - 4
src/rethink/tick_datastore.py

@@ -155,18 +155,19 @@ class TickDataStore(Publisher):
         try:
             self.lock.acquire()
             if contract_key in self.symbols:
-                logging.info('set_symbol_tick_price: ***** sym key= : %s' % contract_key)
-                logging.info('set_symbol_tick_price: ***** sym= : %s' % str(self.symbols[contract_key]['syms']))
+                logging.debug('set_symbol_tick_price: ***** sym key= : %s' % contract_key)
+                logging.debug('set_symbol_tick_price: ***** sym= : %s' % str(self.symbols[contract_key]['syms']))
                 
                 map(lambda e: e.set_tick_value(field, price), self.symbols[contract_key]['syms'])
-                logging.info('set_symbol_tick_price: <<<<<<<<<')
+                
                 self.dispatch(TickDataStore.EVENT_TICK_UPDATED, {'contract_key': contract_key, 'field': field, 
                                                              'price': price, 'syms': self.symbols[contract_key]['syms']})                
                 
                 
         except:
             # contract not set up in the datastore, ignore message
-            logging.error('set_symbol_tick_price: exception occured to: %s' % contract_key)
+            logging.error('tick_datastore:set_symbol_tick_price: exception occured to: %s. Exception could have been triggered due to the dispatched client processing logic' % contract_key)
+            
             #self.dump()
             pass
         finally:

+ 2 - 2
src/sh/start_twsgw.sh

@@ -13,7 +13,7 @@ fi
 export PYTHONPATH=$FINOPT_HOME:$PYTHONPATH
 #  
 # clear all topic offsets and erased saved subscriptions
-python $FINOPT_HOME/comms/ibgw/tws_gateway.py -r -c -f $FINOPT_HOME/config/tws_gateway.cfg 
+#python $FINOPT_HOME/comms/ibgw/tws_gateway.py -r -c -f $FINOPT_HOME/config/tws_gateway.cfg 
 
 
 #
@@ -25,4 +25,4 @@ python $FINOPT_HOME/comms/ibgw/tws_gateway.py -r -c -f $FINOPT_HOME/config/tws_g
 #python $FINOPT_HOME/comms/ibgw/tws_gateway.py  -r -f $FINOPT_HOME/config/tws_gateway.cfg 
 
 # normal restart - keep the offsets and reload from saved subscription entries
-#python $FINOPT_HOME/comms/ibgw/tws_gateway.py   -f $FINOPT_HOME/config/tws_gateway.cfg 
+python $FINOPT_HOME/comms/ibgw/tws_gateway.py   -f $FINOPT_HOME/config/tws_gateway.cfg