def test_add_leg_expection_if_leg_name_duplicate_but_different_assets( self): pos = Position() trans = Transaction(self.fut_contract, self.date, 4.0, 12.3, leg_name='fut1') pos.add(trans) self.assertEqual(True, 'fut1' in pos.legs) self.assertEqual(1, len(pos.legs)) contract_dict = { '_id': '577a4fa34b01f47f84cab23c', 'contractname': 'F.EPZ16', 'cqgsymbol': 'F.EPZ16', 'expirationdate': datetime(2016, 12, 16, 0, 0), 'idcontract': 650, 'idinstrument': 11, 'month': 'Z', 'monthint': 12, 'year': 2016 } fut_contract2 = FutureContract(contract_dict, self.instrument) trans = Transaction(fut_contract2, self.date, -4.0, 12.3, leg_name='fut1') self.assertRaises(NameError, pos.add, trans)
def load_campaign(self, campaign_name, date=None): """ Load campaign net positions for further analysis :param campaign_name: :param date: :return: """ # Load campaign positions campaign_dict = self.datasource.exostorage.campaign_load(campaign_name) if campaign_dict is None: warnings.warn("Campaign not found: " + campaign_name) return cmp = Campaign(campaign_dict, self.datasource) # Calculate campaign's net exo position on particular date pos_date = datetime.now() if date is None else date exo_exposure = cmp.exo_positions(date) transactions = [] for exo_name, exp_dict in exo_exposure.items(): print("Loading: {0} Exposure: {1}".format(exo_name, exp_dict['exposure'])) # Skip zero-positions if exp_dict['exposure'] == 0: continue # Calculate position based on EXO transactions exo_data = self.datasource.exostorage.load_exo(exo_name) if exo_data is None: raise NameError("EXO data for {0} not found.".format(exo_name)) for trans in exo_data['transactions']: if trans['date'] <= pos_date: trans['qty'] *= exp_dict['exposure'] trans['usdvalue'] *= exp_dict['exposure'] transactions.append(trans) else: break # Sort transactions by date transactions = sorted(transactions, key=lambda k: k['date']) # Construct position self.position = Position() for t in transactions: self.position.add_transaction_dict(t) # Convert position to normal state # We will load all assets information from DB # And this will allow us to use position pricing as well self.position.convert(self.datasource, pos_date) # Store positions values for analysis self.position_type = 'Campaign' self.position_name = campaign_name self.analysis_date = pos_date
def test_add_leg_has_legs(self): pos = Position() trans = Transaction(self.fut_contract, self.date, 4.0, 12.3, leg_name='fut1') pos.add(trans) self.assertEqual(True, 'fut1' in pos.legs)
def test_add_leg_has_no_legs_if_leg_name_is_empty(self): pos = Position() trans = Transaction(self.fut_contract, self.date, 4.0, 12.3, leg_name='') pos.add(trans) self.assertEqual(False, 'fut1' in pos.legs)
def test_add(self): pos = Position() trans = Transaction(self.fut_contract, self.date, 4.0, 12.3) pos.add(trans) positions = pos.netpositions self.assertEqual(1, len(positions)) self.assertEqual(True, self.fut_contract in positions) p = positions[self.fut_contract] self.assertEqual(p['qty'], 4.0) self.assertEqual(p['value'], trans.usdvalue) self.assertEqual(0, pos._realized_pnl)
def load_transactions(self, transactions_list, analysis_date, position_name=''): """ Create payoff diagram analysis from transactions list :param transactions_list: :return: """ self.position = Position() for trans in transactions_list: self.position.add(trans) self.position_type = 'TransList' self.position_name = position_name self.analysis_date = analysis_date
def positions(self): """ Returns net positions of campaign on last date :return: """ net_positions = {} for exo_name, exo_exposure in self.exo_positions(date=None).items(): # Load information about EXO positions exo_data = self._datasource.exostorage.load_exo(exo_name) if exo_data is not None: # Get EXO's assets positions exo_pos = Position.get_position_qty(exo_data, self._datasource) for assetname, pos_dict in exo_pos.items(): # Escape special MongoDB keys chars in key names asset_name_safe = assetname.replace('.', '_').replace('$', '_') position = net_positions.setdefault(asset_name_safe, {'asset': pos_dict['asset'], 'qty': 0.0, 'prev_qty': 0.0}) # Multiply EXO position by campaign exposure position['qty'] += pos_dict['qty'] * exo_exposure['exposure'] position['prev_qty'] += float('nan') else: warnings.warn("EXO data not found for " + exo_name) return net_positions
def load(self): exo_data = self.datasource.exostorage.load_exo(self.name) if exo_data is not None: self._position = Position.from_dict(exo_data['position'], self.datasource, self.date) self._old_transactions = exo_data['transactions'] self._series = pickle.loads(exo_data['series']) return exo_data
def test_from_dict(self): pos = Position() trans = Transaction(self.fut_contract, self.date, 4.0, 12.3, leg_name='fut1') pos.add(trans) trans2 = Transaction(self.fut_contract, self.date, -2.0, 13.3) pos.add(trans2) positions = pos.netpositions self.assertEqual(1, len(positions)) self.assertEqual(100, pos._realized_pnl) self.assertEqual(1, len(positions)) self.assertEqual(True, self.fut_contract in positions) p = positions[self.fut_contract] self.assertEqual(p['qty'], 2.0) self.assertEqual(p['value'], trans.usdvalue / 2) self.fut_contract._price = 14.3 self.assertEqual(100, pos._realized_pnl) self.assertEqual(pos.pnl, 100 + 200) pos_dic = pos.as_dict() # Deserealizing position p2 = Position.from_dict(pos_dic, self.datasource, self.date) positions = p2.netpositions self.assertEqual(100, p2._realized_pnl) self.assertEqual(1, len(positions)) self.assertEqual(True, self.fut_contract in positions) p = positions[self.fut_contract] # Wrong - because we have new object from datsource #self.fut_contract._price = 14.3 for k in positions.keys(): k._price = 14.3 self.assertEqual(p['qty'], 2.0) self.assertEqual(p['value'], trans.usdvalue / 2) self.assertEqual(1, len(p2.legs)) self.assertEqual(True, 'fut1' in p2.legs) self.assertEqual(self.fut_contract, p2.legs['fut1']) self.assertEqual(100, p2._realized_pnl) self.assertEqual(p2.pnl, 100 + 200)
def test_close_all(self): pos = Position() trans = Transaction(self.fut_contract, self.date, 4.0, 12.3, leg_name='fut1') pos.add(trans) trans2 = Transaction(self.fut_contract, self.date, 2.0, 15.3) pos.add(trans2) positions = pos.netpositions self.assertEqual(1, len(positions)) self.assertEqual(True, self.fut_contract in positions) p = positions[self.fut_contract] self.assertEqual(p['qty'], 6.0) self.assertEqual(p['value'], trans.usdvalue + trans2.usdvalue) self.assertEqual(0, pos._realized_pnl) self.fut_contract._price = 14.3 trans = pos.close_all_translist() self.assertEqual( [Transaction(self.fut_contract, self.date, -6.0, 14.3)], trans) self.assertEqual('fut1', trans[0].leg_name) self.assertNotEqual(None, Transaction(self.fut_contract, self.date, -6.0, 14.3)) # For 100% coverage
def load_exo(self, exo_name, date=None): """ Load EXO positions for further analysis :param exo_name: Name of EXO to analyze :param date: calculate EXO position on particular date (if None - return current position) :return: None """ # Load EXO dict from EXO engine exo_data = self.datasource.exostorage.load_exo(exo_name) if exo_data is None: raise NameError("EXO data for {0} not found.".format(exo_name)) # Calculate net position on particular date # Reconstruct position passing transactions from early days to current day pos_date = datetime.now() if date is None else date self.position = Position() for trans in exo_data['transactions']: if trans['date'] <= pos_date: self.position.add_transaction_dict(trans) else: break if len(self.position.netpositions) == 0: if len(exo_data['transactions']) == 0: warnings.warn("EXO doesn't contain any transactions") return else: # Can't find any transactions on specific date warnings.warn( "Can't find any transactions on specific date. First EXO transaction occured on {0}" .format(exo_data['transactions'][0]['date'])) return # Convert position to normal state # We will load all assets information from DB # And this will allow us to use position pricing as well self.position.convert(self.datasource, pos_date) self.position_type = 'EXO' self.position_name = exo_name self.analysis_date = pos_date
def test_add_shrink_position_long_and_then_close(self): pos = Position() trans = Transaction(self.fut_contract, self.date, 4.0, 12.3) pos.add(trans) trans2 = Transaction(self.fut_contract, self.date, -2.0, 13.3) pos.add(trans2) trans3 = Transaction(self.fut_contract, self.date, -2.0, 13.3) pos.add(trans3) positions = pos.netpositions self.assertEqual(0, len(positions)) self.assertEqual(200, pos._realized_pnl)
def test_get_position_qty_current_position(self): pos = Position() # # Do sanity checks # self.assertEqual(EXO_POS_DICT_TEST['transactions'][-1]['date'], pd.Timestamp("2016-09-14T12:45:00")) self.assertEqual(EXO_POS_DICT_TEST['transactions'][0]['date'], pd.Timestamp("2011-06-01T12:45:00")) p = pos.get_position_qty(EXO_POS_DICT_TEST, None) self.assertEqual(p["100006570"]['qty'], 1.0) self.assertEqual(p["100006570"]['value'], 105925.0) self.assertEqual(p["203147944"]['qty'], 1.0) self.assertEqual(p["203147944"]['value'], 2067.81315840769) self.assertEqual(p["203147442"]['qty'], -1.0) self.assertEqual(p["203147442"]['value'], -2611.56510629721) self.assertEqual(p["203148813"]['qty'], 1.0) self.assertEqual(p["203148813"]['value'], 3013.95208628632)
def test_add_transaction_dict(self): pos = Position() # # Do sanity checks # self.assertEqual(EXO_POS_DICT_TEST['transactions'][-1]['date'], pd.Timestamp("2016-09-14T12:45:00")) self.assertEqual(EXO_POS_DICT_TEST['transactions'][0]['date'], pd.Timestamp("2011-06-01T12:45:00")) # Reconstruct position from initial transactions for trans_dict in EXO_POS_DICT_TEST['transactions']: pos.add_transaction_dict(trans_dict) self.assertEqual(len(EXO_POS_DICT_TEST['position']['positions']), len(pos.netpositions)) for asset_hash, pos_dict in EXO_POS_DICT_TEST['position'][ 'positions'].items(): self.assertEqual(pos_dict['qty'], pos.netpositions[int(asset_hash)]['qty']) self.assertEqual(pos_dict['value'], pos.netpositions[int(asset_hash)]['value'])
def test_add_to_closeposition_short(self): pos = Position() trans = Transaction(self.fut_contract, self.date, -4.0, 12.3) pos.add(trans) trans = Transaction(self.fut_contract, self.date, 4.0, 13.3) pos.add(trans) positions = pos.netpositions self.assertEqual(0, len(positions)) self.assertEqual(-200, pos._realized_pnl)
def __init__(self, symbol, direction, date, datasource, **kwargs): self._position = Position() self._date = date self._datasource = datasource self._series = pd.DataFrame() self._extra_context = {} self._transactions = [] self._old_transactions = [] log_file_path = kwargs.get('log_file_path', '') is_eod = kwargs.get('is_eod', True) self.debug_mode = log_file_path != '' self.logger = None self.is_eod = is_eod if self.debug_mode: if not os.path.exists(log_file_path): raise ValueError("log_file_path doesn't exists") self.logger = open(os.path.join(log_file_path, self.name+'.log'), 'a') self.logger.write("\nProcessing {0}\n".format(date))
def test_add_2transactions_and_do_reverse_add(self): pos = Position() trans = Transaction(self.fut_contract, datetime(2014, 1, 5, 0, 0, 0), 4.0, 12.3) pos.add(trans) contract_dict = { '_id': '577a4fa34b01f47f84cab23c', 'contractname': 'F.EPZ16', 'cqgsymbol': 'F.EPZ16', 'expirationdate': datetime(2016, 12, 16, 0, 0), 'idcontract': 650, 'idinstrument': 11, 'month': 'Z', 'monthint': 12, 'year': 2016 } fut_contract2 = FutureContract(contract_dict, self.instrument) trans2 = Transaction(fut_contract2, datetime(2014, 1, 6, 0, 0, 0), 2.0, 15.3) pos.add(trans2) # Do offset trade to close previous transaction trans3 = Transaction(fut_contract2, datetime(2014, 1, 7, 0, 0, 0), -2.0, 15.3) pos.add(trans3) positions = pos.netpositions self.assertEqual(1, len(positions)) self.assertEqual(True, self.fut_contract in positions) p = positions[self.fut_contract] self.assertEqual(p['qty'], 4.0) self.assertEqual(0, pos._realized_pnl)
def test_add_shrink_position_short(self): pos = Position() trans = Transaction(self.fut_contract, self.date, -4.0, 12.3) pos.add(trans) trans2 = Transaction(self.fut_contract, self.date, 2.0, 13.3) pos.add(trans2) positions = pos.netpositions self.assertEqual(1, len(positions)) self.assertEqual(-100, pos._realized_pnl) self.assertEqual(1, len(positions)) self.assertEqual(True, self.fut_contract in positions) p = positions[self.fut_contract] self.assertEqual(p['qty'], -2.0) self.assertEqual(p['value'], trans.usdvalue / 2)
def test_as_dict(self): pos = Position() trans = Transaction(self.fut_contract, self.date, 4.0, 12.3, leg_name='leg1') pos.add(trans) trans2 = Transaction(self.fut_contract, self.date, -2.0, 13.3) pos.add(trans2) positions = pos.netpositions self.assertEqual(1, len(positions)) self.assertEqual(100, pos._realized_pnl) self.assertEqual(1, len(positions)) self.assertEqual(True, self.fut_contract in positions) p = positions[self.fut_contract] self.assertEqual(p['qty'], 2.0) self.assertEqual(p['value'], trans.usdvalue / 2) self.fut_contract._price = 14.3 self.assertEqual(100, pos._realized_pnl) self.assertEqual(pos.pnl, 100 + 200) self.assertEqual( { 'positions': { str(self.fut_contract.__hash__()): { 'qty': 2.0, 'value': trans.usdvalue / 2, 'leg_name': 'leg1' } }, '_realized_pnl': 100.0 }, pos.as_dict())
def test_add_leg_delete_leg_is_position_closed(self): pos = Position() trans = Transaction(self.fut_contract, self.date, 4.0, 12.3, leg_name='fut1') pos.add(trans) self.assertEqual(True, 'fut1' in pos.legs) self.assertEqual(1, len(pos.legs)) trans = Transaction(self.fut_contract, self.date, -4.0, 12.3, leg_name='') pos.add(trans) self.assertEqual(False, 'fut1' in pos.legs) self.assertEqual(0, len(pos.legs))
class PayoffAnalyzer: def __init__(self, datasource): self.datasource = datasource self.position = None self.position_type = None self.position_name = None self.analysis_date = None def load_transactions(self, transactions_list, analysis_date, position_name=''): """ Create payoff diagram analysis from transactions list :param transactions_list: :return: """ self.position = Position() for trans in transactions_list: self.position.add(trans) self.position_type = 'TransList' self.position_name = position_name self.analysis_date = analysis_date def load_exo(self, exo_name, date=None): """ Load EXO positions for further analysis :param exo_name: Name of EXO to analyze :param date: calculate EXO position on particular date (if None - return current position) :return: None """ # Load EXO dict from EXO engine exo_data = self.datasource.exostorage.load_exo(exo_name) if exo_data is None: raise NameError("EXO data for {0} not found.".format(exo_name)) # Calculate net position on particular date # Reconstruct position passing transactions from early days to current day pos_date = datetime.now() if date is None else date self.position = Position() for trans in exo_data['transactions']: if trans['date'] <= pos_date: self.position.add_transaction_dict(trans) else: break if len(self.position.netpositions) == 0: if len(exo_data['transactions']) == 0: warnings.warn("EXO doesn't contain any transactions") return else: # Can't find any transactions on specific date warnings.warn( "Can't find any transactions on specific date. First EXO transaction occured on {0}" .format(exo_data['transactions'][0]['date'])) return # Convert position to normal state # We will load all assets information from DB # And this will allow us to use position pricing as well self.position.convert(self.datasource, pos_date) self.position_type = 'EXO' self.position_name = exo_name self.analysis_date = pos_date def load_campaign(self, campaign_name, date=None): """ Load campaign net positions for further analysis :param campaign_name: :param date: :return: """ # Load campaign positions campaign_dict = self.datasource.exostorage.campaign_load(campaign_name) if campaign_dict is None: warnings.warn("Campaign not found: " + campaign_name) return cmp = Campaign(campaign_dict, self.datasource) # Calculate campaign's net exo position on particular date pos_date = datetime.now() if date is None else date exo_exposure = cmp.exo_positions(date) transactions = [] for exo_name, exp_dict in exo_exposure.items(): print("Loading: {0} Exposure: {1}".format(exo_name, exp_dict['exposure'])) # Skip zero-positions if exp_dict['exposure'] == 0: continue # Calculate position based on EXO transactions exo_data = self.datasource.exostorage.load_exo(exo_name) if exo_data is None: raise NameError("EXO data for {0} not found.".format(exo_name)) for trans in exo_data['transactions']: if trans['date'] <= pos_date: trans['qty'] *= exp_dict['exposure'] trans['usdvalue'] *= exp_dict['exposure'] transactions.append(trans) else: break # Sort transactions by date transactions = sorted(transactions, key=lambda k: k['date']) # Construct position self.position = Position() for t in transactions: self.position.add_transaction_dict(t) # Convert position to normal state # We will load all assets information from DB # And this will allow us to use position pricing as well self.position.convert(self.datasource, pos_date) # Store positions values for analysis self.position_type = 'Campaign' self.position_name = campaign_name self.analysis_date = pos_date def calc_payoff(self, strikes_to_analyze=10, iv_change=0.0, days_to_expiration=None): """ Calculates options positions payoff data for graphs (incl. PnL on expiration, current PnL, greeks) :param strikes_to_analyze: number of strikes to show on Payoff graph :param iv_change: IV change in WhatIF scenario :param days_to_expiration: Days to expiration in WhatIF scenario :return: """ if self.position is None: raise Exception( "You should run load_exo() or load_campaign() first") # Get actual price for underlying future contract current_price = self.position.underlying_price # Calculate ATM strike for current price instrument = self.position.underlying atm_strike = instrument.get_atm_strike(current_price) strike_inc = instrument.optionstrikeincrement # Store position's opened value (used for PnL calculation) pos_value = self.position.usdvalue payoffs = [] # Building option payoff diagram for soffset in range(-strikes_to_analyze, strikes_to_analyze, 1): price_to_analyze = atm_strike + soffset * strike_inc # Calculate current payoff of options position whatif_data_current = self.position.price_whatif( underlying_price=price_to_analyze) # Calculate options position value at expiration whatif_data_exp = self.position.price_whatif( underlying_price=price_to_analyze, days_to_expiration=0) # Calculate options position with WhatIF scenario included whatif_data_scenario = self.position.price_whatif( underlying_price=price_to_analyze, iv_change=iv_change, days_to_expiration=days_to_expiration) # Calculate payoff strike_payoff = { 'strike': price_to_analyze, 'current_payoff': whatif_data_current['usdvalue'] - pos_value, 'current_delta': whatif_data_current['delta'], 'expiration_payoff': whatif_data_exp['usdvalue'] - pos_value, 'expiration_delta': whatif_data_exp['delta'], 'scenario_payoff': whatif_data_scenario['usdvalue'] - pos_value, 'scenario_delta': whatif_data_scenario['delta'], } payoffs.append(strike_payoff) dfresult = pd.DataFrame(payoffs) dfresult = dfresult.set_index('strike') return dfresult def position_info(self, iv_change=0.0, days_to_expiration=None): """ Returns net positions values (Qty, Greeks, Prices) :param iv_change: IV change in WhatIF scenario :param days_to_expiration: Days to expiration in WhatIF scenario :return: """ if self.position is None: raise Exception( "You should run load_exo() or load_campaign() first") pos_info = self.position.price_whatif( iv_change=iv_change, days_to_expiration=days_to_expiration) pos_info['opened_value'] = self.position.usdvalue pos_info['current_pnl'] = pos_info['usdvalue'] - self.position.usdvalue pos_info['current_ulprice'] = self.position.underlying_price return pos_info def plot(self, strikes_on_graph, iv_change, days_to_expiration): """ Plot payoff diagram with WhatIF scenario :param strikes_to_analyze: number of strikes to show on Payoff graph :param iv_change: IV change in WhatIF scenario :param days_to_expiration: Days to expiration in WhatIF scenario :return: """ if len(self.position.netpositions) == 0: warnings.warn("Can't calculate payoff diagram for empty position") return dfpayoff = self.calc_payoff(strikes_to_analyze=strikes_on_graph, iv_change=iv_change, days_to_expiration=days_to_expiration) pos_info = self.position_info() f, (ax1, ax2) = plt.subplots(2, gridspec_kw={'height_ratios': [3, 1]}) ax1.set_title('{0}: {1}'.format(self.position_type, self.position_name)) dfpayoff['expiration_payoff'].plot(ax=ax1, label='At expiration', lw=2, c='blue') dfpayoff['current_payoff'].plot(ax=ax1, label='Current', c='green') dfpayoff['scenario_payoff'].plot(ax=ax1, label='WhatIf', style='--', c='red') ax1.axvline(pos_info['current_ulprice'], linestyle='--', c='grey', label='Current price') ax1.axhline(0, c='grey') ax1.legend() ax2.axvline(pos_info['current_ulprice'], linestyle='--', c='grey') dfpayoff['expiration_delta'].plot(ax=ax2, c='blue', lw=2) dfpayoff['current_delta'].plot(ax=ax2, c='green') dfpayoff['scenario_delta'].plot(ax=ax2, style='--', c='red') ax2.set_title('Delta') ax2.axhline(0, c='grey') delta = dfpayoff['expiration_delta'] ax2.set_ylim(delta.min() - 0.2, delta.max() + 0.2) def show_report(self, iv_change, days_to_expiration): if len(self.position.netpositions) == 0: warnings.warn("Can't calculate position report for empty position") return pos_info = self.position_info() print('Position analysis for {0}: {1}\n'.format( self.position_type, self.position_name)) print('Analysis date: {0}'.format(self.analysis_date)) print("PnL: {0:>10}".format(pos_info['current_pnl'])) print("Delta: {0:>10}".format(pos_info['delta'])) df = pd.DataFrame(pos_info['whatif_positions']) display(HTML(self._format_position_table(df))) whatif_pos_info = self.position_info( iv_change=iv_change, days_to_expiration=days_to_expiration) whatifdf = pd.DataFrame(whatif_pos_info['whatif_positions']) display( HTML( self._format_whatif_position_table(whatifdf, iv_change, days_to_expiration))) def _format_position_table(self, posdf): rows = "" table_template = """ <div style="font-family:'Courier New', Courier, monospace;"> <h4>Positions at {1}</h4> <table border="0" cellpadding="10" width="100%"> <thead> <tr> <th style="text-align: center;">Asset</th> <th style="text-align: center;">OpenPrice</th> <th style="text-align: center;">CurrentPrice</th> <th style="text-align: center;">Qty</th> <th style="text-align: center;">PnL</th> <th style="text-align: center;">IV</th> <th style="text-align: center;">Delta</th> <th style="text-align: center;">ToExpiration</th> <th style="text-align: center;">RFR</th> </tr> </thead> {0} </table> </div> """ row_template = ''' <tr> <td>{asset}</td> <td style="text-align: right;">{open_price}</td> <td style="text-align: right;">{price}</td> <td style="text-align: right;">{qty}</td> <td style="text-align: right; {pnl_style}">${pnl:0.0f}</td> <td style="text-align: right;">{iv}</td> <td style="text-align: right;">{delta:0.2f}</td> <td style="text-align: right;">{days_to_expiration} days</td> <td style="text-align: right;">{riskfreerate}</td> </tr> ''' def pnl_color(pnl): if pnl < 0: return 'color: #CC3327;' if pnl > 0: return 'color: #28CC52;' return '' instrument = self.position.underlying def format_price(asset, instrument, price): if asset.startswith('F.'): return round(price, len(str(instrument.ticksize)) - 2) elif asset.startswith('C.') or asset.startswith('P.'): return round(price, len(str(instrument.optionticksize)) - 2) return price def format_iv(iv): if np.isnan(iv): return '' return '{0:0.2f}%'.format(iv * 100) for k, v in posdf.iterrows(): values_dict = v.to_dict() values_dict['open_price'] = format_price(v['asset'], instrument, v['open_price']) values_dict['price'] = format_price(v['asset'], instrument, v['price']) values_dict['pnl_style'] = pnl_color(v['pnl']) values_dict['iv'] = format_iv(v['iv']) values_dict['riskfreerate'] = format_iv(v['riskfreerate']) rows += row_template.format(**values_dict) return table_template.format(rows, self.analysis_date) def _format_whatif_position_table(self, posdf, iv_change, days_to_expiration): rows = "" table_template = """ <div style="font-family:'Courier New', Courier, monospace;"> <h4>WhatIf scenario</h4> <p> IV change: {iv_change} </p> <p> Days to expiration: {days_to_expiration} </p> <table border="0" cellpadding="10" width="100%"> <thead> <tr> <th style="text-align: center;">Asset</th> <th style="text-align: center;">OpenPrice</th> <th style="text-align: center;">CurrentPrice</th> <th style="text-align: center;">Qty</th> <th style="text-align: center;">PnL</th> <th style="text-align: center;">IV</th> <th style="text-align: center;">Delta</th> <th style="text-align: center;">ToExpiration</th> <th style="text-align: center;">RFR</th> </tr> </thead> {rows} </table> </div> """ row_template = ''' <tr> <td>{asset}</td> <td style="text-align: right;">{open_price}</td> <td style="text-align: right;">{price}</td> <td style="text-align: right;">{qty}</td> <td style="text-align: right; {pnl_style}">${pnl:0.0f}</td> <td style="text-align: right;">{iv}</td> <td style="text-align: right;">{delta:0.2f}</td> <td style="text-align: right;">{days_to_expiration} days</td> <td style="text-align: right;">{riskfreerate}</td> </tr> ''' def pnl_color(pnl): if pnl < 0: return 'color: #CC3327;' if pnl > 0: return 'color: #28CC52;' return '' instrument = self.position.underlying def format_price(asset, instrument, price): if asset.startswith('F.'): return round(price, len(str(instrument.ticksize)) - 2) elif asset.startswith('C.') or asset.startswith('P.'): return round(price, len(str(instrument.optionticksize)) - 2) return price def format_iv(iv): if np.isnan(iv): return '' return '{0:0.2f}%'.format(iv * 100) for k, v in posdf.iterrows(): values_dict = v.to_dict() values_dict['open_price'] = format_price(v['asset'], instrument, v['open_price']) values_dict['price'] = format_price(v['asset'], instrument, v['price']) values_dict['pnl_style'] = pnl_color(v['pnl']) values_dict['iv'] = format_iv(v['iv']) values_dict['riskfreerate'] = format_iv(v['riskfreerate']) rows += row_template.format(**values_dict) table_context = { 'rows': rows, 'iv_change': iv_change, 'days_to_expiration': days_to_expiration, } return table_template.format(**table_context)
def test_has_len(self): pos = Position() self.assertEqual(len(pos), 0) trans = Transaction(self.fut_contract, self.date, 4.0, 12.3) pos.add(trans) self.assertEqual(len(pos), 1)