def get_performance_analysis_with_updated_balance( self, # type: HummingbotApplication ) -> PerformanceAnalysis: performance_analysis = PerformanceAnalysis() dedup_set: Set[Tuple[str, str, bool]] = set() for market_symbol_pair in self.market_symbol_pairs: for is_base in [True, False]: for is_starting in [True, False]: market_name = market_symbol_pair.market.name asset_name = market_symbol_pair.base_asset if is_base else market_symbol_pair.quote_asset if len(self.assets) == 0 or len(self.markets) == 0: # Prevent KeyError '***SYMBOL***' amount = self.starting_balances[asset_name][ market_name] else: amount = self.starting_balances[asset_name][market_name] if is_starting \ else self.balance_snapshot()[asset_name][market_name] amount = float(amount) # Adding this check to prevent assets in the same market to be added multiple times if (market_name, asset_name, is_starting) not in dedup_set: dedup_set.add((market_name, asset_name, is_starting)) performance_analysis.add_balances( asset_name, amount, is_base, is_starting) return performance_analysis
def test_calculate_trade_quote_delta_with_fees(self): test_trades = [("BUY", 1, 100), ("SELL", 0.9, 100), ("BUY", 1, 110), ("SELL", 1, 115)] start_time = int(time.time() * 1e3) - 100000 self.save_trade_fill_records(test_trades, self.trading_pair_tuple_1, OrderType.MARKET.name, start_time, self.strategy_1) raw_queried_trades = self.get_trades_from_session(start_time) performance_analysis = PerformanceAnalysis(sql=self.trade_fill_sql) trade_performance_stats, market_trading_pair_stats = performance_analysis.calculate_trade_performance( self.strategy_1, [self.trading_pair_tuple_1], raw_queried_trades, ) expected_trade_performance_stats = { "portfolio_acquired_quote_value": Decimal("24111.45"), "portfolio_delta": Decimal("-823.55"), "portfolio_delta_percentage": Decimal("-3.302787246841788650491277320"), "portfolio_spent_quote_value": Decimal("24935.0") } expected_market_trading_pair_stats = { "acquired_quote_value": Decimal("24111.45"), "asset": { "DAI": { "acquired": Decimal("202.9500000000000021555673912"), "delta": Decimal("-7.0499999999999978444326088"), "delta_percentage": Decimal("-3.357142857142856116396480380"), "spent": Decimal("210") }, "WETH": { "acquired": Decimal("207.8999999999999999562849684"), "delta": Decimal("-7.1000000000000000437150316"), "delta_percentage": Decimal("-3.302325581395348857541875160"), "spent": Decimal("215") } }, "end_quote_rate": Decimal("115.0"), "spent_quote_value": Decimal("24935.0"), "starting_quote_rate": Decimal("1.0"), "trading_pair_delta": Decimal("-823.55000000000000287166124"), "trading_pair_delta_percentage": Decimal("-3.302787246841788662007865410") } self.assertDictEqual(trade_performance_stats, expected_trade_performance_stats) self.assertDictEqual( expected_market_trading_pair_stats, market_trading_pair_stats[self.trading_pair_tuple_1])
def test_calculate_asset_delta_from_trades(self): test_trades = [("BUY", 1, 100), ("SELL", 0.9, 110), ("BUY", 0.1, 100), ("SELL", 1, 120)] start_time = int(time.time() * 1e3) - 100000 self.save_trade_fill_records(test_trades, self.trading_pair_tuple_1, OrderType.MARKET.name, start_time, self.strategy_1) performance_analysis = PerformanceAnalysis(sql=self.trade_fill_sql) raw_queried_trades = self.get_trades_from_session(start_time) market_trading_pair_stats = performance_analysis.calculate_asset_delta_from_trades( self.strategy_1, [self.trading_pair_tuple_1], raw_queried_trades) expected_stats = { 'asset': { 'DAI': { 'acquired': Decimal('216.8100000000000023724772146'), 'spent': Decimal('110.0000000000000005551115123') }, 'WETH': { 'acquired': Decimal('197.9999999999999999583666366'), 'spent': Decimal('230') } }, 'trade_count': 4, 'starting_quote_rate': Decimal('1.0') } self.assertDictEqual( expected_stats, market_trading_pair_stats[self.trading_pair_tuple_1])
def test_calculate_trade_performance(self): test_trades = [("BUY", 2, 100), ("SELL", 0.9, 110), ("BUY", 0.5, 105), ("SELL", 1, 120)] start_time = int(time.time() * 1e3) - 100000 self.save_trade_fill_records(test_trades, self.trading_pair_tuple_1, OrderType.MARKET.name, start_time, self.strategy_1) performance_analysis = PerformanceAnalysis(sql=self.trade_fill_sql) raw_queried_trades = self.get_trades_from_session(start_time) trade_performance_stats, market_trading_pair_stats = performance_analysis.calculate_trade_performance( self.strategy_1, [self.trading_pair_tuple_1], raw_queried_trades, ) expected_trade_performance_stats = { 'portfolio_acquired_quote_value': Decimal('23556.06'), 'portfolio_delta': Decimal('-3146.44'), 'portfolio_delta_percentage': Decimal('-11.78331616889804325437693100'), 'portfolio_spent_quote_value': Decimal('26702.5') } expected_market_trading_pair_stats = { 'acquired_quote_value': Decimal('23556.06'), 'asset': { 'DAI': { 'acquired': Decimal('216.8100000000000023724772146'), 'delta': Decimal('-35.6899999999999976275227854'), 'delta_percentage': Decimal('-14.13465346534653371387041006'), 'spent': Decimal('252.5') }, 'WETH': { 'acquired': Decimal('202.9499999999999999573258025'), 'delta': Decimal('-27.0500000000000000426741975'), 'delta_percentage': Decimal('-11.76086956521739132290182500'), 'spent': Decimal('230') } }, 'end_quote_rate': Decimal('115.0'), 'spent_quote_value': Decimal('26702.5'), 'starting_quote_rate': Decimal('2.0'), 'trading_pair_delta': Decimal('-3146.44000000000000253505550'), 'trading_pair_delta_percentage': Decimal('-11.78331616889804326387063196') } self.assertDictEqual(expected_trade_performance_stats, trade_performance_stats) self.assertDictEqual( expected_market_trading_pair_stats, market_trading_pair_stats[self.trading_pair_tuple_1])
def test_nan_starting(self): """ Test the case where the starting balance is 0. """ performance_analysis = PerformanceAnalysis() performance_analysis.add_balances("WETH", 0, True, True) performance_analysis.add_balances("DAI", 0, False, True) performance_analysis.add_balances("WETH", 0.3, True, False) performance_analysis.add_balances("DAI", 70, False, False) calculated_percent = performance_analysis.compute_profitability(50) self.assertTrue(math.isnan(calculated_percent), "Starting value of 0 test failed.")
def get_performance_analysis_with_updated_balance( self, # type: HummingbotApplication ) -> PerformanceAnalysis: performance_analysis = PerformanceAnalysis() for market_symbol_pair in self.market_symbol_pairs: for is_base in [True, False]: for is_starting in [True, False]: market_name = market_symbol_pair.market.name asset_name = market_symbol_pair.base_asset if is_base else market_symbol_pair.quote_asset amount = self.starting_balances[asset_name][market_name] if is_starting \ else self.balance_snapshot()[asset_name][market_name] amount = float(amount) performance_analysis.add_balances(asset_name, amount, is_base, is_starting) return performance_analysis
def _calculate_trade_performance( self, # type: HummingbotApplication ) -> Tuple[Dict, Dict]: raw_queried_trades = self._get_trades_from_session(self.init_time) current_strategy_name: str = self.markets_recorder.strategy_name performance_analysis: PerformanceAnalysis = PerformanceAnalysis() trade_performance_stats, market_trading_pair_stats = performance_analysis.calculate_trade_performance( current_strategy_name, self.market_trading_pair_tuples, raw_queried_trades, self.starting_balances) return trade_performance_stats, market_trading_pair_stats
def test_multiple_market(self): test_trades_1 = [("BUY", 1, 100), ("SELL", 0.9, 100), ("BUY", 1, 110), ("SELL", 1, 115)] start_time = int(time.time() * 1e3) - 100000 self.save_trade_fill_records(test_trades_1, self.trading_pair_tuple_1, OrderType.MARKET.name, start_time, self.strategy_1) test_trades_2 = [("BUY", 2, 100), ("SELL", 0.9, 110), ("BUY", 0.5, 105), ("SELL", 1, 120)] self.save_trade_fill_records(test_trades_2, self.trading_pair_tuple_2, OrderType.MARKET.name, start_time, self.strategy_1) performance_analysis = PerformanceAnalysis(sql=self.trade_fill_sql) raw_queried_trades = self.get_trades_from_session(start_time) trade_performance_stats, market_trading_pair_stats = performance_analysis.calculate_trade_performance( self.strategy_1, [self.trading_pair_tuple_1, self.trading_pair_tuple_2], raw_queried_trades) expected_trade_performance_stats = { 'portfolio_acquired_quote_value': Decimal('49025.529473684214'), 'portfolio_delta': Decimal('-4151.707368421049'), 'portfolio_delta_percentage': Decimal('-7.807301798602976761843492980'), 'portfolio_spent_quote_value': Decimal('53177.236842105263') } expected_markettrading_pair_stats_1 = { 'acquired_quote_value': Decimal('24111.45'), 'asset': { 'DAI': { 'acquired': Decimal('202.9500000000000021555673912'), 'delta': Decimal('-7.0499999999999978444326088'), 'delta_percentage': Decimal('-3.357142857142856116396480380'), 'spent': Decimal('210') }, 'WETH': { 'acquired': Decimal('207.8999999999999999562849684'), 'delta': Decimal('-7.1000000000000000437150316'), 'delta_percentage': Decimal('-3.302325581395348857541875160'), 'spent': Decimal('215') } }, 'end_quote_rate': Decimal('115.0'), 'spent_quote_value': Decimal('24935.0'), 'starting_quote_rate': Decimal('1.0'), 'trading_pair_delta': Decimal('-823.55000000000000287166124'), 'trading_pair_delta_percentage': Decimal('-3.302787246841788662007865410') } expected_markettrading_pair_stats_2 = { 'acquired_quote_value': Decimal('24914.079473684214'), 'asset': { 'ETH': { 'acquired': Decimal('202.9499999999999999573258025'), 'delta': Decimal('-27.0500000000000000426741975'), 'delta_percentage': Decimal('-11.76086956521739132290182500'), 'spent': Decimal('230') }, 'USDC': { 'acquired': Decimal('216.8100000000000023724772146'), 'delta': Decimal('-35.6899999999999976275227854'), 'delta_percentage': Decimal('-14.13465346534653371387041006'), 'spent': Decimal('252.5') } }, 'end_quote_rate': Decimal('110.0'), 'spent_quote_value': Decimal('28242.236842105263'), 'starting_quote_rate': Decimal('2.0'), 'trading_pair_delta': Decimal('-3011.19000000000000232168451'), 'trading_pair_delta_percentage': Decimal('-11.78432638685060171146339697') } self.assertDictEqual(expected_trade_performance_stats, trade_performance_stats) self.assertDictEqual( expected_markettrading_pair_stats_1, market_trading_pair_stats[self.trading_pair_tuple_1]) self.assertDictEqual( expected_markettrading_pair_stats_2, market_trading_pair_stats[self.trading_pair_tuple_2])
def test_basic_one_ex(self): """ Test performance analysis on a one exchange balance. """ performance_analysis = PerformanceAnalysis() starting_weth = 0.5 starting_dai = 60 current_weth = 0.4 current_dai = 70 performance_analysis.add_balances("WETH", starting_weth, True, True) performance_analysis.add_balances("DAI", starting_dai, False, True) performance_analysis.add_balances("WETH", current_weth, True, False) performance_analysis.add_balances("DAI", current_dai, False, False) calculated_starting_token, calculated_starting_amount = performance_analysis.compute_starting( self._price) calculated_current_token, calculated_current_amount = performance_analysis.compute_current( self._price) calculated_delta_token, calculated_delta_amount = performance_analysis.compute_delta( self._price) calculated_return = performance_analysis.compute_return(self._price) expected_starting_amount = (starting_weth * self._price) + starting_dai expected_current_amount = (current_weth * self._price) + current_dai expected_delta_amount = expected_current_amount - expected_starting_amount expected_return = ( (expected_current_amount / expected_starting_amount) - 1) * 100 self.assertEqual( calculated_starting_token, "DAI", msg= "Basic one exchange test: expected starting token incorrectly determined." ) self.assertAlmostEquals( calculated_starting_amount, expected_starting_amount, msg= "Basic one exchange test: expected starting amount incorrectly determined." ) self.assertEqual( calculated_current_token, "DAI", msg= "Basic one exchange test: expected current token incorrectly determined." ) self.assertAlmostEquals( calculated_current_amount, expected_current_amount, msg= "Basic one exchange test: expected current amount incorrectly determined." ) self.assertEqual( calculated_delta_token, "DAI", msg= "Basic one exchange test: expected delta token incorrectly determined." ) self.assertAlmostEquals( calculated_delta_amount, expected_delta_amount, msg= "Basic one exchange test: expected delta amount incorrectly determined." ) self.assertAlmostEquals( calculated_return, expected_return, msg="Basic one exchange test: return incorrectly determined.")
def test_different_tokens_two_ex(self): """ Test performance analysis on a two exchange balance with different currencies trading. Note that this test will not work as the config file that contains the conversion has not been loaded.""" performance_analysis = PerformanceAnalysis() performance_analysis.add_balances("WETH", 0.5, True, True) performance_analysis.add_balances("DAI", 60, False, True) performance_analysis.add_balances("ETH", 0.7, True, True) performance_analysis.add_balances("USD", 50, False, True) performance_analysis.add_balances("WETH", 0.4, True, False) performance_analysis.add_balances("DAI", 70, False, False) performance_analysis.add_balances("ETH", 0.3, True, False) performance_analysis.add_balances("USD", 70, False, False) calculated_percent = performance_analysis.compute_profitability(50) expected_percent = (((0.7 * 50) + 140)/((1.2 * 50) + 110) - 1) * 100 self.assertAlmostEquals(calculated_percent, expected_percent, msg="Two diff token test failed.", delta=0.1)
def test_calculate_trade_performance(self): test_trades = [("BUY", 100, 2), ("SELL", 110, 0.9), ("BUY", 105, 0.5), ("SELL", 120, 1)] start_time = int(time.time() * 1e3) - 100000 self.save_trade_fill_records(test_trades, self.trading_pair_tuple_1, OrderType.MARKET.name, start_time, self.strategy_1) performance_analysis = PerformanceAnalysis(sql=self.trade_fill_sql) raw_queried_trades = self.get_trades_from_session(start_time) m_name = self.trading_pair_tuple_1.market.name starting_balances = { "DAI": { m_name: Decimal("1000") }, "WETH": { m_name: Decimal("5") } } trade_performance_stats, market_trading_pair_stats = performance_analysis.calculate_trade_performance( self.strategy_1, [self.trading_pair_tuple_1], raw_queried_trades, starting_balances) expected_trade_performance_stats = { 'portfolio_acquired_quote_value': Decimal('501.435'), 'portfolio_spent_quote_value': Decimal('471.0'), 'portfolio_delta': Decimal('30.435'), 'portfolio_delta_percentage': Decimal('1.932380952380952380952380952') } expected_market_trading_pair_stats = { 'starting_quote_rate': Decimal('100.0'), 'asset': { 'WETH': { 'spent': Decimal('1.900000000000000022204460492'), 'acquired': Decimal('2.474999999999999999479582957'), 'delta': Decimal('0.574999999999999977275122465'), 'delta_percentage': Decimal('30.26315789473684055554481720') }, 'DAI': { 'spent': Decimal('252.5'), 'acquired': Decimal('216.8100000000000023724772146'), 'delta': Decimal('-35.6899999999999976275227854'), 'delta_percentage': Decimal('-14.13465346534653371387041006') } }, 'trade_count': 4, 'end_quote_rate': Decimal('115.0'), 'acquired_quote_value': Decimal('501.435'), 'spent_quote_value': Decimal('471.0'), 'starting_quote_value': Decimal('1575.0'), 'trading_pair_delta': Decimal('30.4349999999999997591162981'), 'trading_pair_delta_percentage': Decimal('1.932380952380952365658177657') } self.assertDictEqual(expected_trade_performance_stats, trade_performance_stats) self.assertDictEqual( expected_market_trading_pair_stats, market_trading_pair_stats[self.trading_pair_tuple_1])
def test_basic_two_ex(self): """ Test performance analysis on a two exchange balance with the same currencies trading in both exchanges. """ performance_analysis = PerformanceAnalysis() starting_weth_1 = 0.5 starting_dai_1 = 60 starting_weth_2 = 0.7 starting_dai_2 = 50 current_weth_1 = 0.4 current_dai_1 = 70 current_weth_2 = 0.3 current_dai_2 = 70 performance_analysis.add_balances("WETH", starting_weth_1, True, True) performance_analysis.add_balances("DAI", starting_dai_1, False, True) performance_analysis.add_balances("WETH", starting_weth_2, True, True) performance_analysis.add_balances("DAI", starting_dai_2, False, True) performance_analysis.add_balances("WETH", current_weth_1, True, False) performance_analysis.add_balances("DAI", current_dai_1, False, False) performance_analysis.add_balances("WETH", current_weth_2, True, False) performance_analysis.add_balances("DAI", current_dai_2, False, False) calculated_starting_token, calculated_starting_amount = performance_analysis.compute_starting( self._price) calculated_current_token, calculated_current_amount = performance_analysis.compute_current( self._price) calculated_delta_token, calculated_delta_amount = performance_analysis.compute_delta( self._price) calculated_return = performance_analysis.compute_return(self._price) starting_weth = starting_weth_1 + starting_weth_2 starting_dai = starting_dai_1 + starting_dai_2 current_weth = current_weth_1 + current_weth_2 current_dai = current_dai_1 + current_dai_2 expected_starting_amount = (starting_weth * self._price) + starting_dai expected_current_amount = (current_weth * self._price) + current_dai expected_delta_amount = expected_current_amount - expected_starting_amount expected_return = ( (expected_current_amount / expected_starting_amount) - 1) * 100 self.assertEqual( calculated_starting_token, "DAI", msg= "Basic two exchange test: expected starting token incorrectly determined." ) self.assertAlmostEquals( calculated_starting_amount, expected_starting_amount, msg= "Basic two exchange test: expected starting amount incorrectly determined." ) self.assertEqual( calculated_current_token, "DAI", msg= "Basic two exchange test: expected current token incorrectly determined." ) self.assertAlmostEquals( calculated_current_amount, expected_current_amount, msg= "Basic two exchange test: expected current amount incorrectly determined." ) self.assertEqual( calculated_delta_token, "DAI", msg= "Basic two exchange test: expected delta token incorrectly determined." ) self.assertAlmostEquals( calculated_delta_amount, expected_delta_amount, msg= "Basic two exchange test: expected delta amount incorrectly determined." ) self.assertAlmostEquals( calculated_return, expected_return, msg="Basic two exchange test: return incorrectly determined.")
def trade_performance_report( self, # type: HummingbotApplication ) -> pd.DataFrame: if len(self.market_trading_pair_tuples) == 0: self._notify( " Performance analysis is not available before bot starts") return try: raw_queried_trades = self._get_trades_from_session(self.init_time) current_strategy_name: str = self.markets_recorder.strategy_name primary_quote_asset: str = self.market_trading_pair_tuples[ 0].quote_asset.upper() performance_analysis: PerformanceAnalysis = PerformanceAnalysis() trade_performance_stats, market_trading_pair_stats = performance_analysis.calculate_trade_performance( current_strategy_name, self.market_trading_pair_tuples, raw_queried_trades, ) trade_performance_status_line = [] market_df_data: Set[Tuple[str, str, float, float, str, str]] = set() market_df_columns = [ "Market", "Trading_Pair", "Start_Price", "End_Price", "Total_Value_Delta", "Profit" ] for market_trading_pair_tuple, trading_pair_stats in market_trading_pair_stats.items( ): market_df_data.add(( market_trading_pair_tuple.market.display_name, market_trading_pair_tuple.trading_pair.upper(), float(trading_pair_stats["starting_quote_rate"]), float(trading_pair_stats["end_quote_rate"]), f"{trading_pair_stats['trading_pair_delta']:.8f} {primary_quote_asset}", f"{trading_pair_stats['trading_pair_delta_percentage']:.3f} %" )) inventory_df: pd.DataFrame = self.balance_comparison_data_frame( market_trading_pair_stats) market_df: pd.DataFrame = pd.DataFrame(data=list(market_df_data), columns=market_df_columns) portfolio_delta: Decimal = trade_performance_stats[ "portfolio_delta"] portfolio_delta_percentage: Decimal = trade_performance_stats[ "portfolio_delta_percentage"] trade_performance_status_line.extend(["", " Inventory:"] + [ " " + line for line in inventory_df.to_string().split("\n") ]) trade_performance_status_line.extend( ["", " Market Trading Pair Performance:"] + [" " + line for line in market_df.to_string().split("\n")]) trade_performance_status_line.extend([ "", " Portfolio Performance:" ] + [ f" Quote Value Delta: {portfolio_delta:.7g} {primary_quote_asset}" ] + [f" Delta Percentage: {portfolio_delta_percentage:.3f} %"]) self._notify("\n".join(trade_performance_status_line)) except Exception: self.logger().error( "Unexpected error running performance analysis.", exc_info=True) self._notify("Error running performance analysis")
def test_basic_one_ex(self): """ Test performance analysis on a one exchange balance. """ performance_analysis = PerformanceAnalysis() performance_analysis.add_balances("WETH", 0.5, True, True) performance_analysis.add_balances("DAI", 60, False, True) performance_analysis.add_balances("WETH", 0.4, True, False) performance_analysis.add_balances("DAI", 70, False, False) calculated_percent = performance_analysis.compute_profitability(50) expected_percent = (((0.4 * 50) + 70)/((0.5 * 50) + 60) - 1) * 100 self.assertEqual(calculated_percent, expected_percent, "Basic one ex test failed.")
def test_calculate_trade_quote_delta_with_fees(self): test_trades = [("BUY", 100, 1), ("SELL", 100, 0.9), ("BUY", 110, 1), ("SELL", 115, 1)] start_time = int(time.time() * 1e3) - 100000 self.save_trade_fill_records(test_trades, self.trading_pair_tuple_1, OrderType.MARKET.name, start_time, self.strategy_1) raw_queried_trades = self.get_trades_from_session(start_time) performance_analysis = PerformanceAnalysis(sql=self.trade_fill_sql) m_name = self.trading_pair_tuple_1.market.name starting_balances = { "DAI": { m_name: Decimal("1000") }, "WETH": { m_name: Decimal("5") } } trade_performance_stats, market_trading_pair_stats = performance_analysis.calculate_trade_performance( self.strategy_1, [self.trading_pair_tuple_1], raw_queried_trades, starting_balances) expected_trade_performance_stats = { 'portfolio_acquired_quote_value': Decimal('430.65'), 'portfolio_spent_quote_value': Decimal('428.5'), 'portfolio_delta': Decimal('2.15'), 'portfolio_delta_percentage': Decimal('0.1365079365079365079365079365') } expected_market_trading_pair_stats = { 'starting_quote_rate': Decimal('100.0'), 'asset': { 'WETH': { 'spent': Decimal('1.900000000000000022204460492'), 'acquired': Decimal('1.979999999999999999583666366'), 'delta': Decimal('0.079999999999999977379205874'), 'delta_percentage': Decimal('4.210526315789472444435853800') }, 'DAI': { 'spent': Decimal('210'), 'acquired': Decimal('202.9500000000000021555673912'), 'delta': Decimal('-7.0499999999999978444326088'), 'delta_percentage': Decimal('-3.357142857142856116396480380') } }, 'trade_count': 4, 'end_quote_rate': Decimal('115.0'), 'acquired_quote_value': Decimal('430.65'), 'spent_quote_value': Decimal('428.5'), 'starting_quote_value': Decimal('1575.0'), 'trading_pair_delta': Decimal('2.1499999999999995541760667'), 'trading_pair_delta_percentage': Decimal('0.1365079365079364796302264571') } self.assertDictEqual(trade_performance_stats, expected_trade_performance_stats) self.assertDictEqual( expected_market_trading_pair_stats, market_trading_pair_stats[self.trading_pair_tuple_1])
def test_different_tokens_two_ex(self): """ Test performance analysis on a two exchange balance with different currencies trading. Note that this test will not work as the config file that contains the conversion has not been loaded.""" performance_analysis = PerformanceAnalysis() starting_weth_1 = 0.5 starting_dai_1 = 60 starting_eth_2 = 0.7 starting_usdc_2 = 50 current_weth_1 = 0.4 current_dai_1 = 70 current_eth_2 = 0.3 current_usdc_2 = 70 performance_analysis.add_balances("WETH", starting_weth_1, True, True) performance_analysis.add_balances("DAI", starting_dai_1, False, True) performance_analysis.add_balances("ETH", starting_eth_2, True, True) performance_analysis.add_balances("USDC", starting_usdc_2, False, True) performance_analysis.add_balances("WETH", current_weth_1, True, False) performance_analysis.add_balances("DAI", current_dai_1, False, False) performance_analysis.add_balances("ETH", current_eth_2, True, False) performance_analysis.add_balances("USDC", current_usdc_2, False, False) calculated_starting_token, calculated_starting_amount = performance_analysis.compute_starting( self._price) calculated_current_token, calculated_current_amount = performance_analysis.compute_current( self._price) calculated_delta_token, calculated_delta_amount = performance_analysis.compute_delta( self._price) calculated_return = performance_analysis.compute_return(self._price) starting_weth = starting_weth_1 + starting_eth_2 starting_dai = starting_dai_1 + (starting_usdc_2 * self._usdc_price * (1 / self._dai_price)) current_weth = current_weth_1 + current_eth_2 current_dai = current_dai_1 + (current_usdc_2 * self._usdc_price * (1 / self._dai_price)) expected_starting_amount = (starting_weth * self._price) + starting_dai expected_current_amount = (current_weth * self._price) + current_dai expected_delta_amount = expected_current_amount - expected_starting_amount expected_return = ( (expected_current_amount / expected_starting_amount) - 1) * 100 self.assertEqual( calculated_starting_token, "DAI", msg= "Two exchange test w/ diff tokens: expected starting token incorrectly determined." ) self.assertAlmostEquals( calculated_starting_amount, expected_starting_amount, msg="Two exchange test w/ diff tokens: " "expected starting amount incorrectly determined.") self.assertEqual( calculated_current_token, "DAI", msg= "Two exchange test w/ diff tokens: expected current token incorrectly determined." ) self.assertAlmostEquals( calculated_current_amount, expected_current_amount, msg= "Two exchange test w/ diff tokens: expected current amount incorrectly determined." ) self.assertEqual( calculated_delta_token, "DAI", msg= "Two exchange test w/ diff tokens: expected delta token incorrectly determined." ) self.assertAlmostEquals( calculated_delta_amount, expected_delta_amount, msg= "Two exchange test w/ diff tokens: expected delta amount incorrectly determined." ) self.assertAlmostEquals( calculated_return, expected_return, msg= "Two exchange test w/ diff tokens: return incorrectly determined.")
def test_nan_starting(self): """ Test the case where the starting balance is 0. """ performance_analysis = PerformanceAnalysis() starting_weth = 0 starting_dai = 0 current_weth = 0.3 current_dai = 70 performance_analysis.add_balances("WETH", starting_weth, True, True) performance_analysis.add_balances("DAI", starting_dai, False, True) performance_analysis.add_balances("WETH", current_weth, True, False) performance_analysis.add_balances("DAI", current_dai, False, False) calculated_starting_token, calculated_starting_amount = performance_analysis.compute_starting( self._price) calculated_current_token, calculated_current_amount = performance_analysis.compute_current( self._price) calculated_delta_token, calculated_delta_amount = performance_analysis.compute_delta( self._price) calculated_return = performance_analysis.compute_return(self._price) expected_starting_amount = (starting_weth * self._price) + starting_dai expected_current_amount = (current_weth * self._price) + current_dai expected_delta_amount = expected_current_amount - expected_starting_amount self.assertEqual( calculated_starting_token, "DAI", msg= "Starting value of 0 test: expected starting token incorrectly determined." ) self.assertAlmostEquals( calculated_starting_amount, expected_starting_amount, msg= "Starting value of 0 test: expected starting amount incorrectly determined." ) self.assertEqual( calculated_current_token, "DAI", msg= "Starting value of 0 test: expected current token incorrectly determined." ) self.assertAlmostEquals( calculated_current_amount, expected_current_amount, msg= "Starting value of 0 test: expected current amount incorrectly determined." ) self.assertEqual( calculated_delta_token, "DAI", msg= "Starting value of 0 test: expected delta token incorrectly determined." ) self.assertAlmostEquals( calculated_delta_amount, expected_delta_amount, msg= "Starting value of 0 test: expected delta amount incorrectly determined." ) self.assertTrue( math.isnan(calculated_return), "Starting value of 0 test: return incorrectly determined.")
def test_multiple_market(self): test_trades_1 = [("BUY", 100, 1), ("SELL", 100, 0.9), ("BUY", 110, 1), ("SELL", 115, 1)] start_time = int(time.time() * 1e3) - 100000 self.save_trade_fill_records(test_trades_1, self.trading_pair_tuple_1, OrderType.MARKET.name, start_time, self.strategy_1) test_trades_2 = [("BUY", 100, 2), ("SELL", 110, 0.9), ("BUY", 105, 0.5), ("SELL", 120, 1)] self.save_trade_fill_records(test_trades_2, self.trading_pair_tuple_2, OrderType.MARKET.name, start_time, self.strategy_1) performance_analysis = PerformanceAnalysis(sql=self.trade_fill_sql) raw_queried_trades = self.get_trades_from_session(start_time) m_name_1 = self.trading_pair_tuple_1.market.name m_name_2 = self.trading_pair_tuple_2.market.name starting_balances = { "DAI": { m_name_1: Decimal("1000") }, "WETH": { m_name_1: Decimal("5") }, "USDC": { m_name_2: Decimal("500") }, "ETH": { m_name_2: Decimal("1") } } trade_performance_stats, market_trading_pair_stats = performance_analysis.calculate_trade_performance( self.strategy_1, [self.trading_pair_tuple_1, self.trading_pair_tuple_2], raw_queried_trades, starting_balances) expected_trade_performance_stats = { 'portfolio_acquired_quote_value': Decimal('971.1900000000001'), 'portfolio_spent_quote_value': Decimal('938.5789473684211'), 'portfolio_delta': Decimal('32.6110526315790'), 'portfolio_delta_percentage': Decimal('1.449888849888852211888512345') } expected_markettrading_pair_stats_1 = { 'starting_quote_rate': Decimal('100.0'), 'asset': { 'WETH': { 'spent': Decimal('1.900000000000000022204460492'), 'acquired': Decimal('1.979999999999999999583666366'), 'delta': Decimal('0.079999999999999977379205874'), 'delta_percentage': Decimal('4.210526315789472444435853800') }, 'DAI': { 'spent': Decimal('210'), 'acquired': Decimal('202.9500000000000021555673912'), 'delta': Decimal('-7.0499999999999978444326088'), 'delta_percentage': Decimal('-3.357142857142856116396480380') } }, 'trade_count': 4, 'end_quote_rate': Decimal('115.0'), 'acquired_quote_value': Decimal('430.65'), 'spent_quote_value': Decimal('428.5'), 'starting_quote_value': Decimal('1575.0'), 'trading_pair_delta': Decimal('2.1499999999999995541760667'), 'trading_pair_delta_percentage': Decimal('0.1365079365079364796302264571') } expected_markettrading_pair_stats_2 = { 'starting_quote_rate': Decimal('100.0'), 'asset': { 'ETH': { 'spent': Decimal('1.900000000000000022204460492'), 'acquired': Decimal('2.474999999999999999479582957'), 'delta': Decimal('0.574999999999999977275122465'), 'delta_percentage': Decimal('30.26315789473684055554481720') }, 'USDC': { 'spent': Decimal('252.5'), 'acquired': Decimal('216.8100000000000023724772146'), 'delta': Decimal('-35.6899999999999976275227854'), 'delta_percentage': Decimal('-14.13465346534653371387041006') } }, 'trade_count': 4, 'end_quote_rate': Decimal('110.0'), 'acquired_quote_value': Decimal('540.5400000000001'), 'spent_quote_value': Decimal('510.0789473684211'), 'starting_quote_value': Decimal('674.2105263157895'), 'trading_pair_delta': Decimal('27.5599999999999998727406858'), 'trading_pair_delta_percentage': Decimal('4.518032786885245880777161607') } self.assertDictEqual(expected_trade_performance_stats, trade_performance_stats) self.assertDictEqual( expected_markettrading_pair_stats_1, market_trading_pair_stats[self.trading_pair_tuple_1]) self.assertDictEqual( expected_markettrading_pair_stats_2, market_trading_pair_stats[self.trading_pair_tuple_2])
def test_basic_two_ex(self): """ Test performance analysis on a two exchange balance with the same currencies trading in both exchanges. """ performance_analysis = PerformanceAnalysis() performance_analysis.add_balances("WETH", 0.5, True, True) performance_analysis.add_balances("DAI", 60, False, True) performance_analysis.add_balances("WETH", 0.7, True, True) performance_analysis.add_balances("DAI", 50, False, True) performance_analysis.add_balances("WETH", 0.4, True, False) performance_analysis.add_balances("DAI", 70, False, False) performance_analysis.add_balances("WETH", 0.3, True, False) performance_analysis.add_balances("DAI", 70, False, False) calculated_percent = performance_analysis.compute_profitability(50) expected_percent = (((0.7 * 50) + 140)/((1.2 * 50) + 110) - 1) * 100 self.assertEqual(calculated_percent, expected_percent, "Basic one ex test failed.")