def test_multiple_location_data_and_balances_same_timestamp(user_data_dir): """ Test that adding location and balance data with same timestamp raises an error and no balance/location is added. Regression test for https://github.com/rotki/rotki/issues/1043 """ msg_aggregator = MessagesAggregator() db = DBHandler(user_data_dir, '123', msg_aggregator, None) balances = [ DBAssetBalance( category=BalanceType.ASSET, time=1590676728, asset=A_BTC, amount='1.0', usd_value='8500', ), DBAssetBalance( category=BalanceType.ASSET, time=1590676728, asset=A_BTC, amount='1.1', usd_value='9100', ), ] with pytest.raises(InputError) as exc_info: db.add_multiple_balances(balances) assert 'Adding timed_balance failed.' in str(exc_info.value) assert exc_info.errisinstance(InputError) balances = db.query_timed_balances(from_ts=0, to_ts=1590676728, asset=A_BTC) assert len(balances) == 0 locations = [ LocationData( time=1590676728, location='H', usd_value='55', ), LocationData( time=1590676728, location='H', usd_value='56', ), ] with pytest.raises(InputError) as exc_info: db.add_multiple_location_data(locations) assert 'Tried to add a timed_location_data for' in str(exc_info.value) assert exc_info.errisinstance(InputError) locations = db.get_latest_location_value_distribution() assert len(locations) == 0
def test_timed_balances_primary_key_works(user_data_dir): msg_aggregator = MessagesAggregator() db = DBHandler(user_data_dir, '123', msg_aggregator, None) balances = [ DBAssetBalance( category=BalanceType.ASSET, time=1590676728, asset=A_BTC, amount='1.0', usd_value='8500', ), DBAssetBalance( category=BalanceType.ASSET, time=1590676728, asset=A_BTC, amount='1.1', usd_value='9100', ), ] db.add_multiple_balances(balances) warnings = msg_aggregator.consume_warnings() errors = msg_aggregator.consume_errors() assert len(warnings) == 1 assert len(errors) == 0 balances = db.query_timed_balances(asset=A_BTC) assert len(balances) == 1 balances = [ DBAssetBalance( category=BalanceType.ASSET, time=1590676728, asset=A_ETH, amount='1.0', usd_value='8500', ), DBAssetBalance( category=BalanceType.LIABILITY, time=1590676728, asset=A_ETH, amount='1.1', usd_value='9100', ), ] db.add_multiple_balances(balances) warnings = msg_aggregator.consume_warnings() errors = msg_aggregator.consume_errors() assert len(warnings) == 0 assert len(errors) == 0 balances = db.query_timed_balances(asset=A_ETH) assert len(balances) == 2
def test_timed_balances_primary_key_works(user_data_dir): """ Test that adding two timed_balances with the same primary key i.e (time, currency, category) fails. """ msg_aggregator = MessagesAggregator() db = DBHandler(user_data_dir, '123', msg_aggregator, None) balances = [ DBAssetBalance( category=BalanceType.ASSET, time=1590676728, asset=A_BTC, amount='1.0', usd_value='8500', ), DBAssetBalance( category=BalanceType.ASSET, time=1590676728, asset=A_BTC, amount='1.1', usd_value='9100', ), ] with pytest.raises(InputError) as exc_info: db.add_multiple_balances(balances) assert exc_info.errisinstance(InputError) assert 'Adding timed_balance failed' in str(exc_info.value) balances = db.query_timed_balances(asset=A_BTC) assert len(balances) == 0 balances = [ DBAssetBalance( category=BalanceType.ASSET, time=1590676728, asset=A_ETH, amount='1.0', usd_value='8500', ), DBAssetBalance( category=BalanceType.LIABILITY, time=1590676728, asset=A_ETH, amount='1.1', usd_value='9100', ), ] db.add_multiple_balances(balances) assert len(balances) == 2
def test_multiple_location_data_and_balances_same_timestamp( data_dir, username): """Test that adding location and balance data with same timestamp does not crash. Regression test for https://github.com/rotki/rotki/issues/1043 """ msg_aggregator = MessagesAggregator() username = '******' userdata_dir = os.path.join(data_dir, username) os.mkdir(userdata_dir) db = DBHandler(userdata_dir, '123', msg_aggregator) balances = [ AssetBalance( time=1590676728, asset=A_BTC, amount='1.0', usd_value='8500', ), AssetBalance( time=1590676728, asset=A_BTC, amount='1.1', usd_value='9100', ), ] db.add_multiple_balances(balances) balances = db.query_timed_balances(from_ts=0, to_ts=1590676728, asset=A_BTC) assert len(balances) == 1 locations = [ LocationData( time=1590676728, location='H', usd_value='55', ), LocationData( time=1590676728, location='H', usd_value='56', ), ] db.add_multiple_location_data(locations) locations = db.get_latest_location_value_distribution() assert len(locations) == 1 assert locations[0].usd_value == '55'
class StatisticsFaker(): def __init__(self, args: argparse.Namespace) -> None: self.db = DBHandler( user_data_dir=default_data_directory() / args.user_name, password=args.user_password, msg_aggregator=MessagesAggregator(), initial_settings=None, ) def _clean_tables(self) -> None: cursor = self.db.conn.cursor() cursor.execute('DELETE from timed_location_data;') cursor.execute('DELETE from timed_balances;') self.db.conn.commit() @staticmethod def _get_amounts(args: argparse.Namespace) -> Tuple[int, int, int]: if not isinstance(args.min_amount, int) and args.min_amount < 0: print('Invalid minimum amount given') sys.exit(1) min_amount = args.min_amount if not isinstance(args.max_amount, int) or args.max_amount < min_amount: print('Invalid max amount given') sys.exit(1) max_amount = args.max_amount invalid_starting_amount = (not isinstance(args.starting_amount, int) or args.starting_amount < min_amount or args.starting_amount > max_amount) if invalid_starting_amount: print('Invalid starting amount given') sys.exit(1) starting_amount = args.starting_amount return starting_amount, min_amount, max_amount @staticmethod def _get_timestamps( args: argparse.Namespace) -> Tuple[Timestamp, Timestamp]: if not isinstance(args.from_timestamp, int) or args.from_timestamp < 0: print('Invalid from timestamp given') sys.exit(1) from_ts = Timestamp(args.from_timestamp) if args.to_timestamp is None: to_ts = ts_now() else: if not isinstance(args.to_timestamp, int) or args.to_timestamp < from_ts: print('Invalid to timestamp given') sys.exit(1) to_ts = Timestamp(args.to_timestamp) return from_ts, to_ts def create_fake_data(self, args: argparse.Namespace) -> None: self._clean_tables() from_ts, to_ts = StatisticsFaker._get_timestamps(args) starting_amount, min_amount, max_amount = StatisticsFaker._get_amounts( args) total_amount = starting_amount locations = [ deserialize_location(location) for location in args.locations.split(',') ] assets = [Asset(symbol) for symbol in args.assets.split(',')] go_up_probability = FVal(args.go_up_probability) # Add the first distribution of location data location_data = [] for idx, value in enumerate( divide_number_in_parts(starting_amount, len(locations))): location_data.append( LocationData( time=from_ts, location=locations[idx].serialize_for_db(), usd_value=str(value), )) # add the location data + total to the DB self.db.add_multiple_location_data(location_data + [ LocationData( time=from_ts, location=Location.TOTAL.serialize_for_db(), usd_value=str(total_amount), ) ]) # Add the first distribution of assets assets_data = [] for idx, value in enumerate( divide_number_in_parts(starting_amount, len(assets))): assets_data.append( DBAssetBalance( category=BalanceType.ASSET, time=from_ts, asset=assets[idx], amount=str(random.randint(1, 20)), usd_value=str(value), )) self.db.add_multiple_balances(assets_data) while from_ts < to_ts: print( f'At timestamp: {from_ts}/{to_ts} wih total net worth: ${total_amount}' ) new_location_data = [] new_assets_data = [] from_ts += args.seconds_between_balance_save # remaining_loops = to_ts - from_ts / args.seconds_between_balance_save add_usd_value = random.choice([100, 350, 500, 625, 725, 915, 1000]) add_amount = random.choice([ FVal('0.1'), FVal('0.23'), FVal('0.34'), FVal('0.69'), FVal('1.85'), FVal('2.54'), ]) go_up = ( # If any asset's usd value is close to to go below zero, go up any( FVal(a.usd_value) - FVal(add_usd_value) < 0 for a in assets_data) or # If total is going under the min amount go up total_amount - add_usd_value < min_amount or # If "dice roll" matched and we won't go over the max amount go up (add_usd_value + total_amount < max_amount and FVal(random.random()) <= go_up_probability)) if go_up: total_amount += add_usd_value action = operator.add else: total_amount -= add_usd_value action = operator.sub for idx, value in enumerate( divide_number_in_parts(add_usd_value, len(locations))): new_location_data.append( LocationData( time=from_ts, location=location_data[idx].location, usd_value=str( action(FVal(location_data[idx].usd_value), value)), )) # add the location data + total to the DB self.db.add_multiple_location_data(new_location_data + [ LocationData( time=from_ts, location=Location.TOTAL.serialize_for_db(), usd_value=str(total_amount), ) ]) for idx, value in enumerate( divide_number_in_parts(add_usd_value, len(assets))): old_amount = FVal(assets_data[idx].amount) new_amount = action(old_amount, add_amount) if new_amount < FVal('0'): new_amount = old_amount + FVal('0.01') new_assets_data.append( DBAssetBalance( category=BalanceType.ASSET, time=from_ts, asset=assets[idx], amount=str(new_amount), usd_value=str( action(FVal(assets_data[idx].usd_value), value)), )) self.db.add_multiple_balances(new_assets_data) location_data = new_location_data assets_data = new_assets_data