瀏覽代碼

night commits - continue work on option greeks calculations 90% done

esurfer 9 年之前
父節點
當前提交
07eaa16de3
共有 5 個文件被更改,包括 109 次插入59 次删除
  1. 10 8
      src/finopt/instrument.py
  2. 9 8
      src/finopt/optcal.py
  3. 43 26
      src/rethink/analytics_engine.py
  4. 43 14
      src/rethink/option_chain.py
  5. 4 3
      src/rethink/tick_datastore.py

+ 10 - 8
src/finopt/instrument.py

@@ -57,13 +57,15 @@ class Option(Symbol):
     VEGA     = 5005
     PREMIUM  = 5006
     
-    
+
     #[0,1,2,3,4,5,6,7,8,9,14,5001,5002,5003,5004,5005,5006]
-        
+         
+
     def __init__(self, contract):
         Symbol.__init__(self, contract)
         
-        self.set_analytics(-1.0, -1.0, -1.0, -1.0, -1.0, -1.0)
+        #self.set_analytics(-1.0, -1.0, -1.0, -1.0, -1.0, -1.0)
+        self.set_analytics(float('nan'),float('nan'),float('nan'),float('nan'),float('nan'))
 
         
     def set_analytics(self, imvol=None, delta=None, gamma=None, theta=None, vega=None, npv=None):
@@ -78,11 +80,11 @@ class Option(Symbol):
 #         self.analytics[Option.VEGA] = vega
 #         self.analytics[Option.PREMIUM] = npv
         self.set_tick_value(Option.IMPL_VOL, imvol)
-        self.set_tick_value[Option.DELTA] = delta 
-        self.set_tick_value[Option.GAMMA] = gamma
-        self.set_tick_value[Option.THETA] = theta
-        self.set_tick_value[Option.VEGA] = vega
-        self.set_tick_value[Option.PREMIUM] = npv        
+        self.set_tick_value(Option.DELTA, delta) 
+        self.set_tick_value(Option.GAMMA, gamma)
+        self.set_tick_value(Option.THETA, theta)
+        self.set_tick_value(Option.VEGA, vega)
+        self.set_tick_value(Option.PREMIUM, npv)        
         
         
         

+ 9 - 8
src/finopt/optcal.py

@@ -1,11 +1,12 @@
 # -*- coding: utf-8 -*-
-
 from QuantLib import *
 from bs4 import BeautifulSoup
 from urllib2 import urlopen, Request
 from time import strftime
 import time
 import traceback
+import instrument
+
 
 
 def cal_implvol(spot, strike, callput, evaldate, exdate, rate, div, vol, premium):
@@ -24,9 +25,9 @@ def cal_implvol(spot, strike, callput, evaldate, exdate, rate, div, vol, premium
     process = BlackScholesMertonProcess(S,q,r,sigma)
     im = option.impliedVolatility(premium, process)
     results = {}
-    results['imvol'] = im
+    results[instrument.Option.IMPL_VOL] = im
  
-
+    
     return results
 
 
@@ -49,13 +50,13 @@ def cal_option(spot, strike, callput, evaldate, exdate, rate, div, vol):
     option.setPricingEngine(engine)
             
     results = {}
-    results['npv'] = option.NPV()
+    results[instrument.Option.PREMIUM] = option.NPV()
 
-    results['delta'] = option.delta()
-    results['gamma'] = option.gamma()
+    results[instrument.Option.DELTA] = option.delta()
+    results[instrument.Option.GAMMA] = option.gamma()
     
-    results['theta'] = option.theta() / 365
-    results['vega'] = option.vega() 
+    results[instrument.Option.THETA] = option.theta() / 365
+    results[instrument.Option.VEGA] = option.vega() 
 #    results['rho'] = option.rho() 
 
     results['strikeSensitivity'] = option.strikeSensitivity()

+ 43 - 26
src/rethink/analytics_engine.py

@@ -17,11 +17,7 @@ from comms.ibc.tws_client_lib import TWS_client_manager, AbstractGatewayListener
 
 class AnalyticsEngine(AbstractGatewayListener):
 
-    AE_OPTIONS_CONFIG = {
-        'underlying_substitution': {'IND': 'FUT'},
-        'underlying_sub_list': ['HSI', 'MHI']
-    }
-    
+  
     
     
     def __init__(self, kwargs):
@@ -63,13 +59,23 @@ class AnalyticsEngine(AbstractGatewayListener):
         
     
     def test_oc3(self, oc3):
-        expiry = '20170330'
-        contractTuple = ('HHI.HK', 'FUT', 'HKFE', 'HKD', expiry, 0, '')
+#         expiry = '20170330'
+#         contractTuple = ('HHI.HK', 'FUT', 'HKFE', 'HKD', expiry, 0, '')
+#         contract = ContractHelper.makeContract(contractTuple)  
+#         
+#         oc3.set_option_structure(contract, 200, 50, 0.0012, 0.0328, expiry)        
+#         
+#         oc3.build_chain(10445, 0.03, 0.22)
+
+        expiry = '20170331'
+        contractTuple = ('QQQ', 'STK', 'SMART', 'USD', '', 0, '')
+
+
         contract = ContractHelper.makeContract(contractTuple)  
         
-        oc3.set_option_structure(contract, 200, 50, 0.0012, 0.0328, expiry)        
+        oc3.set_option_structure(contract, 0.5, 100, 0.0012, 0.0328, expiry)        
         
-        oc3.build_chain(10445, 0.03, 0.22)
+        oc3.build_chain(130, 0.03, 0.22)
         
 #         expiry='20170324'
 #         contractTuple = ('QQQ', 'STK', 'SMART', 'USD', '', 0, '')
@@ -146,24 +152,35 @@ class AnalyticsEngine(AbstractGatewayListener):
     def tds_event_tick_updated(self, event, contract_key, field, price, syms):
         results = {}
         for s in syms:
-            chain_id = s.get_extra_attributes(OptionsChain.CHAIN_IDENTIFIER) 
-            if chain_id  in self.option_chains.keys():
-                if 'FUT' in contract_key:
-                    results = self.option_chains[chain_id].cal_greeks_in_chain(self.kwargs['evaluation_date'])
-                else:
-                    results[ContractHelper.makeRedisKeyEx(s.get_contract())] = self.option_chains[chain_id].cal_option_greeks(s, self.kwargs['evaluation_date'])
-        
-            
-        # set_analytics(self, imvol=None, delta=None, gamma=None, theta=None, vega=None, npv=None):
-        # 
-        def update_tds_analytics(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_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 OptionsChain.CHAIN_IDENTIFIER in s.get_extra_attributes():
+                
+                chain_id = s.get_extra_attributes()[OptionsChain.CHAIN_IDENTIFIER]
+                logging.info('AnalyticsEngine:tds_event_tick_updated chain_id %s' % chain_id)
+                if chain_id  in self.option_chains.keys():
+                    if 'FUT' in contract_key or 'STK' in contract_key:
+                        results = self.option_chains[chain_id].cal_greeks_in_chain(self.kwargs['evaluation_date'])
+                    else:
+                        results[ContractHelper.makeRedisKeyEx(s.get_contract())] = self.option_chains[chain_id].cal_option_greeks(s, self.kwargs['evaluation_date'])
+                logging.info('AnalysticsEngine: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):
+                    
+                    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_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()))                
+
+            else:
+                
+                continue
+             
+        
+
 
     def tds_event_symbol_deleted(self, event, update_mode, name, instrument):
         pass

+ 43 - 14
src/rethink/option_chain.py

@@ -11,7 +11,7 @@ from misc2.observer import NotImplementedException
 
 
 from time import sleep
-from finopt.optcal import cal_implvol
+from finopt.optcal import cal_implvol, cal_option
 
 
 class OptionsChain(Publisher):
@@ -49,7 +49,12 @@ class OptionsChain(Publisher):
     '''
     EVENT_OPTION_UPDATED = 'oc_option_updated'
     EVENT_UNDERLYING_ADDED = 'oc_underlying_added'
-    OC_EVENTS = [EVENT_OPTION_UPDATED, EVENT_UNDERLYING_ADDED]     
+    OC_EVENTS = [EVENT_OPTION_UPDATED, EVENT_UNDERLYING_ADDED]    
+    EMPTY_GREEKS =   {Option.DELTA: float('nan'), Option.GAMMA: float('nan'), 
+                      Option.THETA: float('nan'), Option.VEGA: float('nan'),
+                      Option.IMPL_VOL: float('nan'), Option.PREMIUM: float('nan')}   
+
+     
     
     def __init__(self, name):
         self.name = name
@@ -203,17 +208,41 @@ class OptionsChain(Publisher):
             key = ContractHelper.makeRedisKeyEx(o.get_contract())
             greeks = self.cal_option_greeks(o, valuation_date)
             all_results[key] = greeks
-    
+
         return all_results
     
-    def cal_option_greeks(self, o, valuation_date):
+    def cal_option_greeks(self, option, valuation_date):
+        
+        uspot_last = self.get_underlying().get_tick_value(4)
+        if uspot_last is None:
+            return OptionsChain.EMPTY_GREEKS
+        o = option.get_contract()
+        logging.info('OptionChain:cal_option_greeks. %8.4f' % uspot_last)
+
+            
+            
         try:
-            uspot_last = self.get_underlying().get_tick_value(4)
-            greeks = cal_implvol(uspot_last, o.m_strike, o.m_right, valuation_date, 
-                                  o.m_expiry, self.rate, self.div, self.trade_vol, o.get_tick_value(4))
+            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))
+        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))
+            iv = cal_implvol(uspot_last, o.m_strike, o.m_right, valuation_date, 
+                                  o.m_expiry, self.rate, self.div, self.trade_vol, abs(uspot_last - o.m_strike))
+
+        try:                
+            greeks = cal_option(uspot_last, o.m_strike, o.m_right, valuation_date, 
+                                  o.m_expiry, self.rate, self.div, iv[Option.IMPL_VOL])
+            greeks.update(iv)
+            logging.info('OptionChain:cal_option_greeks. %s' % greeks)
         
         except Exception, err:
-            logging.error(traceback.format_exc())        
+            logging.error('OptionsChain:cal_option_greeks. Error retrieving uspot_last  greeks for option %s' % ContractHelper.makeRedisKeyEx(o))
+            logging.error(traceback.format_exc())     
+            greeks = {Option.DELTA: float('nan'), Option.GAMMA: float('nan'), 
+                      Option.THETA: float('nan'), Option.VEGA: float('nan'),
+                      Option.IMPL_VOL: float('nan'), Option.PREMIUM: float('nan')}   
+
 
         return greeks
      
@@ -245,9 +274,9 @@ class OptionsChain(Publisher):
                                                format_tick_val(x[1].get_tick_value(1), fmt_spec),
                                                format_tick_val(x[1].get_tick_value(2), fmt_spec),
                                                format_tick_val(x[1].get_tick_value(3), fmt_specq),
-                                               format_tick_val(x[1].get_analytics()[Option.IMPL_VOL], fmt_spec2),
-                                               format_tick_val(x[1].get_analytics()[Option.DELTA], fmt_spec2),
-                                               format_tick_val(x[1].get_analytics()[Option.THETA], fmt_spec2),
+                                               format_tick_val(x[1].get_tick_value(Option.IMPL_VOL), fmt_spec2),
+                                               format_tick_val(x[1].get_tick_value(Option.DELTA), fmt_spec2),
+                                               format_tick_val(x[1].get_tick_value(Option.THETA), fmt_spec2),
                                                )), sorted_call)
         
         fmt_put = map(lambda x: (x[0], '%s,%s,%s,%s,%s,%s,%s,%s' % (format_tick_val(x[1].get_tick_value(4), fmt_spec),
@@ -255,9 +284,9 @@ class OptionsChain(Publisher):
                                                format_tick_val(x[1].get_tick_value(1), fmt_spec),
                                                format_tick_val(x[1].get_tick_value(2), fmt_spec),
                                                format_tick_val(x[1].get_tick_value(3), fmt_specq),
-                                               format_tick_val(x[1].get_analytics()[Option.IMPL_VOL], fmt_spec2),
-                                               format_tick_val(x[1].get_analytics()[Option.DELTA], fmt_spec2),
-                                               format_tick_val(x[1].get_analytics()[Option.THETA], fmt_spec2),                    
+                                               format_tick_val(x[1].get_tick_value(Option.IMPL_VOL), fmt_spec2),
+                                               format_tick_val(x[1].get_tick_value(Option.DELTA), fmt_spec2),
+                                               format_tick_val(x[1].get_tick_value(Option.THETA), fmt_spec2),                    
                                                )), sorted_put)
         
         undlypx = '%s,%s,%s,%s,%s' % (format_tick_val(self.get_underlying().get_tick_value(4), fmt_spec), 

+ 4 - 3
src/rethink/tick_datastore.py

@@ -156,16 +156,17 @@ class TickDataStore(Publisher):
             self.lock.acquire()
             if contract_key in self.symbols:
                 map(lambda e: e.set_tick_value(field, price), self.symbols[contract_key]['syms'])
+                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_price: exception occured to: %s' % contract_key)
+            logging.error('set_symbol_tick_price: exception occured to: %s' % contract_key)
             #self.dump()
             pass
         finally:
             self.lock.release()
-            self.dispatch(TickDataStore.EVENT_TICK_UPDATED, {'contract_key': contract_key, 'field': field, 
-                                                             'price': price, 'syms': self.symbols[contract_key]['syms']})
+
             
 
     def set_symbol_analytics(self, contract_key, field, value):