Browse Source

untested logic to handle AE options calculations

bobhk 9 năm trước cách đây
mục cha
commit
ef5a5844ff

+ 38 - 15
src/finopt/instrument.py

@@ -39,14 +39,23 @@ class Symbol():
         return self.key
     
 class Option(Symbol):
+    """
+        Tick Value      Description
+        # 5001            impl vol
+        # 5002            delta
+        # 5003            gamma
+        # 5004            theta
+        # 5005            vega
+        # 5006            premium    
+    """
     
-    analytics = None
-    IMPL_VOL = 'imvol'
-    DELTA    = 'delta'
-    GAMMA    = 'gamma'
-    THETA    = 'theta'
-    VEGA     = 'vega'
-    PREMIUM  = 'npv'
+    
+    IMPL_VOL = 5001
+    DELTA    = 5002
+    GAMMA    = 5003
+    THETA    = 5004
+    VEGA     = 5005
+    PREMIUM  = 5006
     
     
     #[0,1,2,3,4,5,6,7,8,9,14,5001,5002,5003,5004,5005,5006]
@@ -60,22 +69,34 @@ class Option(Symbol):
     def set_analytics(self, imvol=None, delta=None, gamma=None, theta=None, vega=None, npv=None):
         
         
-        if self.analytics == None:
-            self.analytics = {}           
-        self.analytics[Option.IMPL_VOL] = imvol
-        self.analytics[Option.DELTA] = delta 
-        self.analytics[Option.GAMMA] = gamma
-        self.analytics[Option.THETA] = theta
-        self.analytics[Option.VEGA] = vega
-        self.analytics[Option.PREMIUM] = npv
+#         if self.analytics == None:
+#             self.analytics = {}           
+#         self.analytics[Option.IMPL_VOL] = imvol
+#         self.analytics[Option.DELTA] = delta 
+#         self.analytics[Option.GAMMA] = gamma
+#         self.analytics[Option.THETA] = theta
+#         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        
+        
         
         
     def get_analytics(self):
+        raise Exception
+    
         return self.analytics
     
     
     def object2kvstring(self):
         
+        raise Exception
+    
+    
         try:           
             kv = self.object2kv()
             return '{"%s":%s, "%s":%s, "%s":%s, "%s":%s}' % ('analytics', dict2str(kv['analytics']), 
@@ -89,6 +110,8 @@ class Option(Symbol):
     
     
     def object2kv(self):
+        raise Exception
+        
         try:
             analytics = self.get_analytics()
             contract =  self.get_contract()

+ 97 - 25
src/finopt/optcal.py

@@ -224,8 +224,79 @@ def get_HSI_last_trading_day(holidays, month, year):
 #     return map(lambda x: deduce_last_trading_day(Date.endOfMonth(Date(1, x, year) + Period("1m"))), range(1, 12))
 #     #mm_dd = deduce_last_trading_day(Date.endOfMonth(Date.todaysDate())) 
 #     #return mm_dd
+
+
+def test3(spot, strike, callput, evaldate, exdate, rate, div, vol):
+    
+    Settings.instance().evaluationDate = str2qdate(evaldate)
+    exercise = EuropeanExercise(str2qdate(exdate))
+    payoff = PlainVanillaPayoff(str2qopt_type(callput), strike)
+    option = EuropeanOption(payoff,exercise)
+    r = YieldTermStructureHandle(FlatForward(str2qdate(evaldate), rate, Actual365Fixed()))
+    q = YieldTermStructureHandle(FlatForward(str2qdate(evaldate), div, Actual365Fixed()))
     
+    S = QuoteHandle(SimpleQuote(spot))
+#    r = YieldTermStructureHandle(FlatForward(0, HongKong(), rate, Actual365Fixed()))
+#    q = YieldTermStructureHandle(FlatForward(0, HongKong(), div, Actual365Fixed()))
 
+#    sigma = BlackVolTermStructureHandle(BlackConstantVol(0, HongKong(), vol, Actual365Fixed()))
+    sigma = BlackVolTermStructureHandle(BlackConstantVol(str2qdate(evaldate), HongKong(), vol, Actual365Fixed()))
+    process = BlackScholesMertonProcess(S,q,r,sigma)
+    engine = AnalyticEuropeanEngine(process)
+    option.setPricingEngine(engine)
+    
+    start_time = time.time()
+    
+    for i in range(100):
+        results = cal_option(spot+i, 24200, 'C', '20170327', '20170330', 0.00012, 0.0328, 0.120)
+        results = {}
+        results['npv'] = option.NPV()
+    
+        results['delta'] = option.delta()
+        results['gamma'] = option.gamma()
+        
+        results['theta'] = option.theta() / 365
+        results['vega'] = option.vega() 
+    #    results['rho'] = option.rho() 
+    
+        results['strikeSensitivity'] = option.strikeSensitivity()
+   # results['thetaPerDay'] = option.thetaPerDay()
+   # results['itmCashProbability'] = option.itmCashProbability()
+ 
+    elapsed_time = time.time() - start_time
+    print elapsed_time           
+ 
+ 
+
+    return results
+    
+
+def test():
+    start_time = time.time()
+    results = cal_option(24290.0, 24200, 'C', '20170327', '20170330', 0.00012, 0.0328, 0.120)
+    elapsed_time = time.time() - start_time
+    print 'elapsed time: %5.6f' % elapsed_time
+    print ''.join ('%s=%0.4f, '%(k,v) for k, v in results.iteritems())
+    
+    start_time = time.time()
+    results = cal_implvol(24290.0, 24200, 'C', '20170327', '20170330', 0.00012, 0.0328, 0.0, results['npv'])
+    elapsed_time = time.time() - start_time
+    print 'elapsed time: %5.6f' % elapsed_time
+    print ''.join ('%s=%0.4f, '%(k,v) for k, v in results.iteritems())
+    
+    print 'end of test 1'
+    
+def test2():
+    start_time = time.time()
+    for i in range(100):
+        results = cal_option(100+i, 24200, 'C', '20170327', '20170330', 0.00012, 0.0328, 0.120)
+    elapsed_time = time.time() - start_time
+    print 'elapsed time: %5.6f' % elapsed_time
+
+    print 'end of test 2'
+    
+    
+    
 if __name__ == '__main__':
     
     
@@ -265,8 +336,9 @@ if __name__ == '__main__':
     #spot 24119.0, X 25000, right: P, evaldate: 20150812, expiry: 20150828, rate: 0.0005, div: 0.0005, vol: 0.2000, premium: 334.0000
     #spot 24149.0, X 25200, right: P, evaldate: 20150812, expiry: 20150828, rate: 0.0005, div: 0.0005, vol: 0.2000, premium: 437.5000
     
-    results = cal_option(22363.0, 22000, 'C', '20151201', '20151230', 0.00012, 0.0328, 0.198)
-    print ''.join ('%s=%0.4f, '%(k,v) for k, v in results.iteritems())
+    test()
+    test2()
+    test3(100, 24200, 'C', '20170327', '20170330', 0.00012, 0.0328, 0.120)
 #     results = cal_option(23067.0, 22000, 'P', '20151018', '20151029', 0.0009, 0.0328, 0.2918)
 #     npv1 = results['npv']
 #     v1 = 0.2918
@@ -309,26 +381,26 @@ if __name__ == '__main__':
 #     print chk.advance(Date(17, October, 2015), 1, 2)
     #print get_HSI_expiry(2016)
     
-    holidays = get_hk_holidays(2017)
-
-    
-    
-    month_names = [January,
-                February,
-                March,
-                April,
-                May,
-                June,
-                July,
-                August,
-                September,
-                October,
-                November,
-                December,
-                ] 
-    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)
+#     holidays = get_hk_holidays(2017)
+# 
+#     
+#     
+#     month_names = [January,
+#                 February,
+#                 March,
+#                 April,
+#                 May,
+#                 June,
+#                 July,
+#                 August,
+#                 September,
+#                 October,
+#                 November,
+#                 December,
+#                 ] 
+#     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)

+ 72 - 14
src/rethink/analytics_engine.py

@@ -1,15 +1,16 @@
 import logging
 import json
+import time, datetime
 import copy
 from optparse import OptionParser
 from time import sleep
 from misc2.observer import Subscriber
 from misc2.helpers import ContractHelper
-from finopt.instrument import Symbol
+from finopt.instrument import Symbol, Option
 from rethink.option_chain import OptionsChain
 from rethink.tick_datastore import TickDataStore
 from comms.ibc.tws_client_lib import TWS_client_manager, AbstractGatewayListener
-from comms.ibgw.base_messaging import BaseMessageListener
+
 
 
 
@@ -32,13 +33,13 @@ class AnalyticsEngine(AbstractGatewayListener):
         self.tds.register_listener(self)
         self.twsc.add_listener_topics(self, kwargs['topics'])
         
- 
+        
         self.option_chains = {}
         
     
     def test_oc(self, oc2):
         expiry = '20170330'
-        contractTuple = ('HSI', 'FUT', 'HKFE', 'HKD', '', 0, expiry)
+        contractTuple = ('HSI', 'FUT', 'HKFE', 'HKD', expiry, 0, '')
         contract = ContractHelper.makeContract(contractTuple)  
         
         oc2.set_option_structure(contract, 200, 50, 0.0012, 0.0328, expiry)        
@@ -61,12 +62,41 @@ class AnalyticsEngine(AbstractGatewayListener):
         self.tds.add_symbol(oc2.get_underlying())
         
     
+    def test_oc3(self, oc3):
+        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='20170324'
+#         contractTuple = ('QQQ', 'STK', 'SMART', 'USD', '', 0, '')
+#         contract = ContractHelper.makeContract(contractTuple)  
+# 
+#         oc2.set_option_structure(contract, 0.5, 100, 0.0012, 0.0328, expiry)        
+#     
+#         oc2.build_chain(132.11, 0.02, 0.22)
+        
+        
+        oc3.pretty_print()        
+
+        for o in oc3.get_option_chain():
+            self.tds.add_symbol(o)
+        self.tds.add_symbol(oc3.get_underlying())
+        
+    
     def start_engine(self):
         self.twsc.start_manager()
         oc2 = OptionsChain('oc2')
         oc2.register_listener(self)
-        
         self.test_oc(oc2)
+        oc3 = OptionsChain('oc3')
+        oc3.register_listener(self)
+        self.test_oc3(oc3)
+        self.option_chains[oc2.name] = oc2
+        self.option_chains[oc3.name] = oc3
         
         try:
             logging.info('AnalyticsEngine:main_loop ***** accepting console input...')
@@ -75,6 +105,8 @@ class AnalyticsEngine(AbstractGatewayListener):
 
                 read_ch = raw_input("Enter command:")
                 oc2.pretty_print()
+                oc3.pretty_print()
+                self.tds.dump()
                 sleep(0.45)
             
         except (KeyboardInterrupt, SystemExit):
@@ -111,11 +143,27 @@ class AnalyticsEngine(AbstractGatewayListener):
         #logging.info('tds_event_new_symbol_added. %s' % ContractHelper.object2kvstring(symbol.get_contract()))
         
     
-    def tds_event_tick_updated(self, event, contract_key, field, price, canAutoExecute):
-        #tds_event_tick_updated:
-        # dict object: {'partition': 0, 'value': '{"field": 7, "price": 35.0, "canAutoExecute": 0, "tickerId": 10}', 'offset': 527}
-        #logging.info('tds_event_tick_updated. %s' % items)
-        pass
+    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()))
 
     def tds_event_symbol_deleted(self, event, update_mode, name, instrument):
         pass
@@ -135,11 +183,12 @@ class AnalyticsEngine(AbstractGatewayListener):
     #
 
     def tickPrice(self, event, contract_key, field, price, canAutoExecute):
-        logging.info('MessageListener:%s. %s %d %8.2f' % (event, contract_key, field, price))
+        logging.debug('MessageListener:%s. %s %d %8.2f' % (event, contract_key, field, price))
         self.tds.set_symbol_tick_price(contract_key, field, price, canAutoExecute)
 
+
     def tickSize(self, event, contract_key, field, size):
-        self.tds.set_symbol_tick_price(contract_key, field, size, 0)
+        self.tds.set_symbol_tick_size(contract_key, field, size)
         #logging.info('MessageListener:%s. %s: %d %8.2f' % (event, contract_key, field, size))
  
     def error(self, event, message_value):
@@ -164,8 +213,9 @@ if __name__ == '__main__':
       'session_timeout_ms': 10000,
       'clear_offsets':  False,
       'logconfig': {'level': logging.INFO, 'filemode': 'w', 'filename': '/tmp/ae.log'},
-      'topics': ['tickPrice'],
-      'seek_to_end': ['*'],
+      'topics': ['tickPrice', 'tickSize'],
+      'seek_to_end': ['*']
+
       #'seek_to_end':['tickSize', 'tickPrice','gw_subscriptions', 'gw_subscription_changed']
       }
 
@@ -176,11 +226,19 @@ if __name__ == '__main__':
     parser.add_option("-g", "--group_id",
                       action="store", dest="group_id", 
                       help="assign group_id to this running instance")
+    parser.add_option("-e", "--evaluation_date",
+                     action="store", dest="evaluation_date", 
+                     help="specify evaluation date for option calculations")   
     
     (options, args) = parser.parse_args()
+    if options.evaluation_date == None:
+        options.evaluation_date = time.strftime('%Y%m%d') 
+    
     for option, value in options.__dict__.iteritems():
         if value <> None:
             kwargs[option] = value
+    
+    
             
   
       

+ 27 - 1
src/rethink/option_chain.py

@@ -1,4 +1,5 @@
 # -*- coding: utf-8 -*-
+import sys, traceback
 import json
 import logging
 from ib.ext.Contract import Contract
@@ -10,7 +11,7 @@ from misc2.observer import NotImplementedException
 
 
 from time import sleep
-import finopt.optcal
+from finopt.optcal import cal_implvol
 
 
 class OptionsChain(Publisher):
@@ -29,6 +30,7 @@ class OptionsChain(Publisher):
     trade_vol = None
     #iv = optcal.cal_implvol(spot, contract.m_strike, contract.m_right, today, contract.m_expiry, rate, div, vol, premium)
     
+    CHAIN_IDENTIFIER = 'chain_identifier'
     
     
     '''
@@ -71,6 +73,7 @@ class OptionsChain(Publisher):
     def set_underlying(self, contract):
         #self.underlying = contract
         self.underlying = Symbol(contract)
+        self.underlying.set_extra_attributes(OptionsChain.CHAIN_IDENTIFIER, self.name)
 
         
         
@@ -184,6 +187,7 @@ class OptionsChain(Publisher):
         #events = ('on_option_added', 'on_option_deleted', 'on_option_updated')
         #
         # 
+        option.set_extra_attributes(OptionsChain.CHAIN_IDENTIFIER, self.name)
         self.options.append(option)
         self.dispatch(OptionsChain.EVENT_OPTION_UPDATED, {'update_mode': 'A', 
                                                             'name': self.name,
@@ -191,6 +195,28 @@ class OptionsChain(Publisher):
                       )
     
     
+    
+    def cal_greeks_in_chain(self, valuation_date):
+        
+        all_results = {}
+        for o in self.options:
+            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):
+        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))
+        
+        except Exception, err:
+            logging.error(traceback.format_exc())        
+
+        return greeks
+     
     def pretty_print(self):
         sorted_opt = sorted(map(lambda i: (self.options[i].get_contract().m_strike, self.options[i]) , range(len(self.options))))
         

BIN
src/rethink/option_chain.pyc


+ 36 - 7
src/rethink/tick_datastore.py

@@ -151,11 +151,6 @@ class TickDataStore(Publisher):
        
         
     def set_symbol_tick_price(self, contract_key, field, price, canAutoExecute):   
-        
-        # message_value: dict: '{"tickerId": 0, "size": 3, "field": 3}'
-        
-        
-        
         logging.debug('set_symbol_price: -------------------')
         try:
             self.lock.acquire()
@@ -170,13 +165,47 @@ class TickDataStore(Publisher):
         finally:
             self.lock.release()
             self.dispatch(TickDataStore.EVENT_TICK_UPDATED, {'contract_key': contract_key, 'field': field, 
-                                                             'price': price, 'canAutoExecute': canAutoExecute})
+                                                             'price': price, 'syms': self.symbols[contract_key]['syms']})
             
 
+    def set_symbol_analytics(self, contract_key, field, value):
+        logging.debug('set_symbol_analytics: -------------------')
+        try:
+            self.lock.acquire()
+            if contract_key in self.symbols:
+                map(lambda e: e.set_tick_value(field, value), 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)
+            #self.dump()
+            pass
+        finally:
+            self.lock.release()
 
-
         
+    def set_symbol_tick_size(self, contract_key, field, size): 
+        
+  
+        
+        logging.debug('set_symbol_size: -------------------')
+        try:
+            self.lock.acquire()
+            if contract_key in self.symbols:
+                map(lambda e: e.set_tick_value(field, size), self.symbols[contract_key]['syms'])
             
+        except:
+            # contract not set up in the datastore, ignore message
+            logging.error('set_symbol_size: 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, 
+            #                                                 'size': size})
+            
+        
+
 
 
 

+ 2 - 2
src/sh/gw_ex_request_exit.sh

@@ -9,5 +9,5 @@ else
 	FINOPT_HOME=~/l1304/workspace/finopt-ironfly/finopt/src
 fi
 export PYTHONPATH=$FINOPT_HOME:$PYTHONPATH
-python $FINOPT_HOME/comms/ibc/gw_ex_request_exit.py -g AAA -n dumpty 
-#python $FINOPT_HOME/comms/ibc/gw_ex_request_exit.py -c -g AAA -n dumpty 
+#python $FINOPT_HOME/comms/ibc/gw_ex_request_exit.py -g AAA -n dumpty 
+python $FINOPT_HOME/comms/ibc/gw_ex_request_exit.py -c -g AAA -n dumpty 

+ 2 - 2
src/sh/start_twsgw.sh

@@ -11,7 +11,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 
 
 
 #
@@ -23,4 +23,4 @@ export PYTHONPATH=$FINOPT_HOME:$PYTHONPATH
 #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