laxaurus 9 gadi atpakaļ
vecāks
revīzija
f66ffa5dc7
3 mainītis faili ar 233 papildinājumiem un 67 dzēšanām
  1. 229 67
      src/rethink/portfolio_monitor.py
  2. 2 0
      src/sh/ae.sh
  3. 2 0
      src/sh/start_twsgw.sh

+ 229 - 67
src/rethink/portfolio_monitor.py

@@ -10,16 +10,79 @@ from finopt.instrument import Symbol, Option
 from rethink.option_chain import OptionsChain
 from rethink.option_chain import OptionsChain
 from rethink.tick_datastore import TickDataStore
 from rethink.tick_datastore import TickDataStore
 from comms.ibc.tws_client_lib import TWS_client_manager, AbstractGatewayListener
 from comms.ibc.tws_client_lib import TWS_client_manager, AbstractGatewayListener
+from numpy import average
 
 
 
 
 
 
+class PortfolioItem():
+    """
+        Set up some constant variables
+        
+        position
+        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
+    
+        
+    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
+        
+        contract = ContractHelper.makeContractfromRedisKeyEx(contract_key)
+        if contract.m_secType == 'OPT':
+            self.instrument = Option(contract)
+        else: 
+            self.instrument = Symbol(contract)
+        
+    
+    def get_instrument(self):
+        return self.instrument
+        
+    def get_instrument_type(self):
+        return self.instrument.get_contract().m_secType
+    
+    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 set_position_cost(self, position, average_cost):
+        self.quantity = position
+        self.average_cost = average_cost   
 
 
 
 
 class PortfolioMonitor(AbstractGatewayListener):
 class PortfolioMonitor(AbstractGatewayListener):
 
 
   
   
-    
-    
+    '''
+        portfolios : 
+             {
+                <account_id>: {'port_items': {<contract_key>, PortItem}, 'opt_chains': {<oc_id>: option_chain}}
+             }   
+                
+    '''
+    rule_map = {
+                '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}
+                                        
+                                    }
+               }    
+
     def __init__(self, kwargs):
     def __init__(self, kwargs):
         self.kwargs = copy.copy(kwargs)
         self.kwargs = copy.copy(kwargs)
         self.twsc = TWS_client_manager(kwargs)
         self.twsc = TWS_client_manager(kwargs)
@@ -29,28 +92,18 @@ class PortfolioMonitor(AbstractGatewayListener):
         self.tds.register_listener(self)
         self.tds.register_listener(self)
         self.twsc.add_listener_topics(self, kwargs['topics'])
         self.twsc.add_listener_topics(self, kwargs['topics'])
         
         
-        
+        self.portfolios = {}
         self.option_chains = {}
         self.option_chains = {}
         
         
     
     
-    def create_option_chain(self):
-        '''
-            'underlying': 
-        '''
-        pass
-    
-    
-    def update_option_chain(self, chain_id):
-        pass
-    
     def test_oc(self, oc2):
     def test_oc(self, oc2):
-        expiry = '20170330'
+        expiry = '20170427'
         contractTuple = ('HSI', 'FUT', 'HKFE', 'HKD', expiry, 0, '')
         contractTuple = ('HSI', 'FUT', 'HKFE', 'HKD', expiry, 0, '')
         contract = ContractHelper.makeContract(contractTuple)  
         contract = ContractHelper.makeContract(contractTuple)  
         
         
         oc2.set_option_structure(contract, 200, 50, 0.0012, 0.0328, expiry)        
         oc2.set_option_structure(contract, 200, 50, 0.0012, 0.0328, expiry)        
         
         
-        oc2.build_chain(24119, 0.03, 0.22)
+        oc2.build_chain(24172, 0.04, 0.22)
         
         
 #         expiry='20170324'
 #         expiry='20170324'
 #         contractTuple = ('QQQ', 'STK', 'SMART', 'USD', '', 0, '')
 #         contractTuple = ('QQQ', 'STK', 'SMART', 'USD', '', 0, '')
@@ -68,59 +121,24 @@ class PortfolioMonitor(AbstractGatewayListener):
         self.tds.add_symbol(oc2.get_underlying())
         self.tds.add_symbol(oc2.get_underlying())
         
         
     
     
-    def test_oc3(self, oc3):
-        expiry = '20170427'
-        contractTuple = ('HSI', 'FUT', 'HKFE', 'HKD', expiry, 0, '')
-        contract = ContractHelper.makeContract(contractTuple)  
-         
-        oc3.set_option_structure(contract, 200, 50, 0.0012, 0.0328, expiry)        
-         
-        oc3.build_chain(24380, 0.03, 0.22)
-
-#         expiry = '20170331'
-#         contractTuple = ('QQQ', 'STK', 'SMART', 'USD', '', 0, '')
-# 
-# 
-#         contract = ContractHelper.makeContract(contractTuple)  
-#         
-#         oc3.set_option_structure(contract, 0.5, 100, 0.0012, 0.0328, expiry)        
-#         
-#         oc3.build_chain(130, 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):
     def start_engine(self):
         self.twsc.start_manager()
         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
+#         oc2 = OptionsChain('oc2')
+#         oc2.register_listener(self)
+#         self.test_oc(oc2)
+#         self.option_chains[oc2.name] = oc2
         
         
         try:
         try:
             logging.info('PortfolioMonitor:main_loop ***** accepting console input...')
             logging.info('PortfolioMonitor:main_loop ***** accepting console input...')
             menu = {}
             menu = {}
-            menu['1']="Display option chain <id>" 
-            menu['2']="Display tick data store "
-            menu['3']="No opt"
-            menu['4']="Exit"
+            menu['1']="Request position" 
+            menu['2']=""
+            menu['3']=""
+            menu['4']=""
+            menu['9']="Exit"
             while True: 
             while True: 
                 choices=menu.keys()
                 choices=menu.keys()
                 choices.sort()
                 choices.sort()
@@ -129,16 +147,14 @@ class PortfolioMonitor(AbstractGatewayListener):
 
 
                 selection = raw_input("Enter command:")
                 selection = raw_input("Enter command:")
                 if selection =='1':
                 if selection =='1':
-                    oc2.pretty_print()
+                    self.twsc.reqPositions()
                 elif selection == '2': 
                 elif selection == '2': 
                     self.tds.dump()
                     self.tds.dump()
-                elif selection == '3':
-                    oc3.pretty_print()
-                elif selection == '4': 
+                elif selection == '9': 
                     self.twsc.gw_message_handler.set_stop()
                     self.twsc.gw_message_handler.set_stop()
                     break
                     break
                 else: 
                 else: 
-                    oc3.pretty_print()                
+                    pass                
                 
                 
                 sleep(0.15)
                 sleep(0.15)
             
             
@@ -147,6 +163,141 @@ class PortfolioMonitor(AbstractGatewayListener):
             self.twsc.gw_message_handler.set_stop() 
             self.twsc.gw_message_handler.set_stop() 
             logging.info('PortfolioMonitor: Service shut down complete...')               
             logging.info('PortfolioMonitor: Service shut down complete...')               
     
     
+    def is_contract_in_portfolio(self, account, contract_key):
+        return self.get_portfolio_port_items(account, contract_key)
+            
+    def get_portfolio_port_items(self, account, contract_key):
+        try:
+            return self.portfolios[account]['port_items'][contract_key]
+        except KeyError:
+            return None
+    
+    def set_portfolio_port_items(self, account, contract_key, port_item):
+        self.portfolios[account]['port_items'][contract_key] = port_item
+        
+        
+    def create_empty_portfolio(self, account):
+        port = self.portfolios[account] = {}
+        self.portfolios[account]['port_items']=  {}
+        self.portfolios[account]['opt_chains']=  {}
+        return port
+                
+    def get_portfolio(self, account):
+        try:
+            return self.portfolios[account]
+        except KeyError:
+            self.portfolios[account] = self.create_empty_portfolio(account)
+        return self.portfolios[account]
+    
+    def deduce_option_underlying(self, option):
+        '''
+            given an Option object, return the underlying Symbol object
+        '''
+        
+
+        try:
+            symbol_id = option.get_contract().m_symbol
+            underlying_sectype = self.rule_map['symbol'][symbol_id]
+            exchange = option.get_contract().m_exchange
+            currency = option.get_contract().m_currency
+            expiry = option.get_contract().m_expiry if self.rule_map['expiry'][symbol_id] ==  'same_month' else ''
+            contractTuple = (symbol_id, underlying_sectype, exchange, currency, expiry, 0, '')
+            logging.info('PortfolioMonitor:deduce_option_underlying. Deduced underlying==> %s' %
+                          ContractHelper.printContract(contractTuple))
+            return Symbol(ContractHelper.makeContract(contractTuple))
+        except KeyError:
+            logging.error('PortfolioMonitor:deduce_option_underlying. Unable to deduce the underlying for the given option %s' %
+                          ContractHelper.printContract(option.get_contract))
+            return None
+        
+        
+    def is_oc_in_portfolio(self, account, oc_id):
+        try:
+            return self.portfolios[account]['opt_chains'][oc_id]
+        except KeyError:
+            return None
+        
+        
+    def get_portfolio_option_chain(self, account, underlying):
+        
+        
+        def create_oc_id(account, underlying_id, month):
+            return '%s-%s-%s' % (account, underlying_id, month)
+        
+        underlying_id = underlying.get_contract().m_symbol
+        month = underlying.get_contract().m_expiry
+        oc_id = create_oc_id(account, underlying_id, month)
+        oc = self.is_oc_in_portfolio(account, oc_id)
+        if oc == None:
+            oc = OptionsChain(oc_id)
+            oc.set_option_structure(underlying,
+                                    self.rule_map['option_structure'][underlying_id]['spd_size'],
+                                    self.rule_map['option_structure'][underlying_id]['multiplier'],
+                                    self.rule_map['option_structure'][underlying_id]['rate'],
+                                    self.rule_map['option_structure'][underlying_id]['div'],
+                                    month)
+            
+            self.portfolios[account]['opt_chains'][oc_id] = oc 
+            
+            
+        return oc
+    
+    
+    
+    def process_position(self, account, contract_key, position, average_cost):
+        
+        # obtain a reference to the portfolio, if not exist create a new one 
+        port = self.get_portfolio(account)
+        port_item =  self.is_contract_in_portfolio(account, contract_key)
+
+            
+            
+        if port_item:
+            # update the values and recalculate p/l
+            port_item.set_position(position, average_cost)
+            port_item.calculate_pl()
+        # new position 
+        else:
+            port_item = PortfolioItem(account, contract_key, position, average_cost)
+            port['port_items'][contract_key] = port_item
+            instrument = port_item.get_instrument()
+            self.tds.add_symbol(instrument)
+            self.twsc.reqMktData(instrument.get_contract(), True)
+            # option position
+            if port_item.get_instrument_type() == 'OPT':
+                '''
+                    deduce option's underlying
+                    resolve associated option chain by month, underlying
+                    
+                '''
+                underlying = self.deduce_option_underlying(instrument)
+                if underlying:
+                    oc = self.get_portfolio_option_chain(account, underlying)
+                    oc.register_listener(self)
+                    instrument.set_extra_attributes(OptionsChain.CHAIN_IDENTIFIER, oc.get_name())
+                    oc.add_option(instrument)
+                    
+                    
+                else:
+                    logging.error('PortfolioMonitor:process_position. **** Error in adding the new position %s' % contract_key)
+            # non options. stocks, futures that is...    
+            else:
+                port['port_items'][contract_key] = port_item
+                
+            self.dump_portfolio(account)    
+        
+    def dump_portfolio(self, account):
+        #<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, 
+                                           ContractHelper.printContract(x[1].get_instrument()))
+        
+        p_items = map(print_port_items, list(self.portfolios[account]['port_items']))
+        return '\n'.join(p_items)
+        
+         
     
     
     #         EVENT_OPTION_UPDATED = 'oc_option_updated'
     #         EVENT_OPTION_UPDATED = 'oc_option_updated'
     #         EVENT_UNDERLYING_ADDED = 'oc_underlying_added
     #         EVENT_UNDERLYING_ADDED = 'oc_underlying_added
@@ -234,6 +385,17 @@ class PortfolioMonitor(AbstractGatewayListener):
         self.tds.set_symbol_tick_size(contract_key, field, size)
         self.tds.set_symbol_tick_size(contract_key, field, size)
         #logging.info('MessageListener:%s. %s: %d %8.2f' % (event, contract_key, field, size))
         #logging.info('MessageListener:%s. %s: %d %8.2f' % (event, contract_key, field, size))
  
  
+    def position(self, event, account, contract_key, position, average_cost, end_batch):
+        if not end_batch:
+            logging.info('PortfolioMonitor:position. received position message contract=%s' % contract_key)
+            self.process_position(account, contract_key, position, average_cost)
+   
+    def positionEnd(self, event): #, message_value):
+        """ generated source for method positionEnd """
+        logging.info('%s [[ %s ]]' % (event, vars()))
+
+ 
+ 
     def error(self, event, message_value):
     def error(self, event, message_value):
         logging.info('PortfolioMonitor:%s. val->[%s]' % (event, message_value))         
         logging.info('PortfolioMonitor:%s. val->[%s]' % (event, message_value))         
         
         
@@ -256,10 +418,10 @@ if __name__ == '__main__':
       'session_timeout_ms': 10000,
       'session_timeout_ms': 10000,
       'clear_offsets':  False,
       'clear_offsets':  False,
       'logconfig': {'level': logging.INFO, 'filemode': 'w', 'filename': '/tmp/pm.log'},
       'logconfig': {'level': logging.INFO, 'filemode': 'w', 'filename': '/tmp/pm.log'},
-      'topics': ['tickPrice', 'tickSize'],
+      'topics': ['position', 'positionEnd'],
       'seek_to_end': ['*']
       'seek_to_end': ['*']
 
 
-      #'seek_to_end':['tickSize', 'tickPrice','gw_subscriptions', 'gw_subscription_changed']
+      
       }
       }
 
 
     usage = "usage: %prog [options]"
     usage = "usage: %prog [options]"

+ 2 - 0
src/sh/ae.sh

@@ -5,6 +5,8 @@ HOST=$(hostname)
 echo $HOST
 echo $HOST
 if [ $HOST == 'hkc-larryc-vm1' ]; then
 if [ $HOST == 'hkc-larryc-vm1' ]; then
 	FINOPT_HOME=~/ironfly-workspace/finopt/src
 	FINOPT_HOME=~/ironfly-workspace/finopt/src
+elif [ $HOST == 'vorsprung' ]; then
+	FINOPT_HOME=~/workspace/finopt/src	
 else
 else
 	FINOPT_HOME=~/l1304/workspace/finopt-ironfly/finopt/src
 	FINOPT_HOME=~/l1304/workspace/finopt-ironfly/finopt/src
 fi
 fi

+ 2 - 0
src/sh/start_twsgw.sh

@@ -5,6 +5,8 @@ HOST=$(hostname)
 echo $HOST
 echo $HOST
 if [ $HOST == 'hkc-larryc-vm1' ]; then
 if [ $HOST == 'hkc-larryc-vm1' ]; then
 	FINOPT_HOME=~/ironfly-workspace/finopt/src
 	FINOPT_HOME=~/ironfly-workspace/finopt/src
+elif [ $HOST == 'vorsprung' ]; then
+	FINOPT_HOME=~/workspace/finopt/src
 else
 else
 	FINOPT_HOME=~/l1304/workspace/finopt-ironfly/finopt/src
 	FINOPT_HOME=~/l1304/workspace/finopt-ironfly/finopt/src
 fi
 fi