We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Show all user trades/actions in a new page
## Problem Definition

We have the user data action but users can't actually explore it in any way in the UI. That's not good and we should definitely offer a way to display all actions/trades in a dedicated page.

That would mean showing user's:
- Exchange trades
- Exchange deposit/withdrawals
- DEX trades (when we support them)


## Considerations

Number of visible actions will be limited whether user has premium or not. Need to think a bit here on how to make this a premium feature but also available to normal users. At the moment can only think of limit in the number of trades/actions shown.

## Task

## Backend
The backend already has an api call for this. It's actually being used with a filter for external location for the OTC trades.

## Frontend

The frontend need a new dedicated page. User tactions or something like that. And there all trades, deposits/withdrawals of all locations should be displayed. This is going to be a very long list in most cases so we should think on optimizations and how to show the data in visually appealing and also useful way.

I would imagine it would make sense to have different tables for trades, different for asset movements (deposit/withdrawals) etc. And perhaps a differen component per location. Or simply offer filtering.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `tools/data_faker/data_faker/actions.py`
Content:
```
1 import logging
2 import random
3 from typing import Tuple
4 
5 from rotkehlchen.assets.asset import Asset
6 from rotkehlchen.balances.manual import ManuallyTrackedBalance
7 from rotkehlchen.constants.assets import A_BTC, A_EUR, A_USD, FIAT_CURRENCIES
8 from rotkehlchen.exchanges.data_structures import Trade, TradeType
9 from rotkehlchen.fval import FVal
10 from rotkehlchen.history import PriceHistorian
11 from rotkehlchen.serialization.deserialize import deserialize_location, pair_get_assets
12 from rotkehlchen.typing import Location, Timestamp, TradePair
13 
14 STARTING_TIMESTAMP = 1464739200  # 01/06/2016
15 NUMBER_OF_TRADES = 5
16 STARTING_FUNDS = {A_EUR: FVal(100000), A_BTC: FVal(10)}
17 
18 MAX_TRADE_DIFF_VARIANCE = 14400
19 
20 ALLOWED_EXCHANGES = ['kraken', 'binance']
21 KRAKEN_PAIRS = [TradePair('ETH_EUR'), TradePair('BTC_EUR')]
22 
23 MAX_TRADE_USD_VALUE = FVal(100)
24 MAX_FEE_USD_VALUE = 1
25 
26 
27 logger = logging.getLogger(__name__)
28 
29 
30 class ActionWriter():
31 
32     def __init__(
33             self,
34             trades_number: int,
35             seconds_between_trades: int,
36             seconds_between_balance_save: int,
37             rotkehlchen,
38             fake_kraken,
39             fake_binance,
40     ):
41         self.seconds_between_trades = seconds_between_trades
42         self.seconds_between_balance_save = seconds_between_balance_save
43         self.trades_number = trades_number
44         self.current_ts = STARTING_TIMESTAMP
45         self.last_trade_ts = 0
46         self.last_balance_save_ts = 0
47         self.funds = STARTING_FUNDS
48         self.rotki = rotkehlchen
49         self.kraken = fake_kraken
50         self.binance = fake_binance
51 
52         timestamp, _, _ = self.get_next_ts()
53         for asset, value in self.funds.items():
54             if asset.is_fiat():
55                 self.rotki.data.db.add_manually_tracked_balances([ManuallyTrackedBalance(
56                     asset=asset,
57                     label=f'{asset.identifier} balance',
58                     amount=value,
59                     location=Location.BANKS,
60                     tags=None,
61                 )])
62         self.rotki.query_balances(requested_save_data=True, timestamp=timestamp)
63 
64         # divide our starting funds between exchanges and keep a part out
65         divide_by = len(ALLOWED_EXCHANGES) + 1
66         for asset, value in self.funds.items():
67             amount = value / divide_by
68             for exchange in ALLOWED_EXCHANGES:
69                 timestamp, _, _ = self.get_next_ts()
70 
71                 skip_exchange = asset in FIAT_CURRENCIES and exchange != 'kraken'
72 
73                 if not skip_exchange:
74                     getattr(self, exchange).deposit(
75                         asset=asset,
76                         amount=amount,
77                         time=timestamp,
78                     )
79                 if asset in FIAT_CURRENCIES:
80                     self.rotki.data.db.add_manually_tracked_balances([ManuallyTrackedBalance(
81                         asset=asset,
82                         label=f'{asset.identifier} balance',
83                         amount=value,
84                         location=Location.BANKS,
85                         tags=None,
86                     )])
87 
88         self.rotki.query_balances(requested_save_data=True, timestamp=timestamp)
89         self.last_balance_save_ts = timestamp
90 
91     def maybe_save_balances(self, save_ts: Timestamp) -> None:
92         """Maybe Save all current balances in the fake user's DB at the current timestamp
93 
94         If the save_ts is not after the time we save balances then nothing happens
95         """
96         if save_ts - self.last_balance_save_ts < self.seconds_between_balance_save:
97             return
98         self.rotki.query_balances(requested_save_data=True, timestamp=save_ts)
99         self.last_balance_save_ts = save_ts
100 
101     def generate_history(self) -> None:
102         created_trades = 0
103         while created_trades <= self.trades_number:
104             current_ts, save_balances, make_trade = self.get_next_ts()
105 
106             if make_trade:
107                 self.create_action(created_trades, current_ts)
108                 created_trades += 1
109 
110             if save_balances:
111                 self.maybe_save_balances(save_ts=current_ts)
112 
113     @staticmethod
114     def query_historical_price(from_asset: Asset, to_asset: Asset, timestamp: Timestamp):
115         return PriceHistorian().query_historical_price(
116             from_asset=from_asset,
117             to_asset=to_asset,
118             timestamp=timestamp,
119         )
120 
121     def increase_asset(self, asset: Asset, amount: FVal, exchange: str) -> None:
122         if asset not in self.funds:
123             self.funds[asset] = amount
124         else:
125             self.funds[asset] += amount
126 
127         getattr(self, exchange).increase_asset(asset, amount)
128 
129     def decrease_asset(self, asset: Asset, amount: FVal, exchange: str) -> None:
130         assert asset in self.funds, 'Asset should exist in funds'
131         assert amount <= self.funds[asset], 'We should have enough funds to decrease asset'
132         self.funds[asset] -= amount
133 
134         getattr(self, exchange).decrease_asset(asset, amount)
135 
136     def get_next_ts(self) -> Tuple[Timestamp, bool, bool]:
137         current_ts = self.current_ts
138         advance_by_secs = min(self.seconds_between_trades, self.seconds_between_balance_save)
139         secs_in_future = random.randint(
140             advance_by_secs,
141             advance_by_secs + MAX_TRADE_DIFF_VARIANCE,
142         )
143         self.current_ts += secs_in_future
144 
145         save_balances = False
146         if self.current_ts - self.last_balance_save_ts >= self.seconds_between_balance_save:
147             save_balances = True
148 
149         make_trade = False
150         if self.current_ts - self.last_trade_ts >= self.seconds_between_trades:
151             make_trade = True
152 
153         return Timestamp(current_ts), save_balances, make_trade
154 
155     def create_action(self, index: int, ts: Timestamp):
156         """Create a random trade action on a random exchange depending
157         on the funds that are available in that exchange"""
158         # choose an exchange at random
159         exchange_name = random.choice(ALLOWED_EXCHANGES)
160         exchange = getattr(self, exchange_name)
161         # choose a random pair at that exchange
162         pair = exchange.choose_pair(
163             timestamp=ts,
164             price_query=self.query_historical_price,
165         )
166         print(
167             f'Creating trade {index + 1} / {self.trades_number} in {exchange_name}'
168             f' for the pair: {pair} at timestamp {ts}',
169         )
170         # depending on our funds decide on what to do. Buy/sell
171         base, quote = pair_get_assets(pair)
172         if exchange.get_balance(base) is None:
173             action_type = TradeType.BUY
174         elif exchange.get_balance(quote) is None:
175             action_type = TradeType.SELL
176         else:
177             # TODO: trade the one we have most of
178             action_type = random.choice(list(TradeType))
179 
180         # if we are buying we are going to spend from the quote asset
181         if action_type == TradeType.BUY:
182             spending_asset = quote
183         else:  # selling spends from the base asset
184             spending_asset = base
185         # get a spending asset amount within our per-trade equivalent range and
186         # our available funds
187         spending_usd_rate = self.query_historical_price(spending_asset, A_USD, ts)
188         max_usd_in_spending_asset = spending_usd_rate * exchange.get_balance(spending_asset)
189         max_usd_equivalent_to_spend = min(max_usd_in_spending_asset, MAX_TRADE_USD_VALUE)
190         rate = self.query_historical_price(base, quote, ts)
191         usd_to_spend = FVal(random.uniform(0.01, float(max_usd_equivalent_to_spend)))
192         amount_in_spending_asset = usd_to_spend / spending_usd_rate
193         # if we are buying then the amount is the amount of asset we bought
194         if action_type == TradeType.BUY:
195             amount = amount_in_spending_asset / rate
196         # if we are selling the amount is the spending asset amount
197         else:
198             amount = amount_in_spending_asset
199 
200         quote_asset_usd_rate = self.query_historical_price(quote, A_USD, ts)
201         fee_in_quote_currency = FVal(random.uniform(0, MAX_FEE_USD_VALUE)) / quote_asset_usd_rate
202 
203         # create the trade
204         trade = Trade(
205             timestamp=ts,
206             location=deserialize_location(exchange_name),
207             pair=pair,
208             trade_type=action_type,
209             amount=amount,
210             rate=rate,
211             fee=fee_in_quote_currency,
212             fee_currency=quote,
213             link='',
214             notes='',
215         )
216         logger.info(f'Created trade: {trade}')
217 
218         # Adjust our global and per exchange accounting
219         if action_type == TradeType.BUY:
220             # we buy so we increase our base asset by amount
221             self.increase_asset(base, amount, exchange_name)
222             # and decrease quote by amount * rate
223             self.decrease_asset(quote, amount * rate, exchange_name)
224         else:
225             # we sell so we increase our quote asset
226             self.increase_asset(quote, amount * rate, exchange_name)
227             # and decrease our base asset
228             self.decrease_asset(base, amount, exchange_name)
229 
230         # finally add it to the exchange
231         exchange.append_trade(trade)
232
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/tools/data_faker/data_faker/actions.py b/tools/data_faker/data_faker/actions.py
--- a/tools/data_faker/data_faker/actions.py
+++ b/tools/data_faker/data_faker/actions.py
@@ -79,7 +79,7 @@
                 if asset in FIAT_CURRENCIES:
                     self.rotki.data.db.add_manually_tracked_balances([ManuallyTrackedBalance(
                         asset=asset,
-                        label=f'{asset.identifier} balance',
+                        label=f'{asset.identifier} balance {timestamp}',
                         amount=value,
                         location=Location.BANKS,
                         tags=None,
@@ -104,8 +104,11 @@
             current_ts, save_balances, make_trade = self.get_next_ts()
 
             if make_trade:
-                self.create_action(created_trades, current_ts)
-                created_trades += 1
+                try:
+                    self.create_action(created_trades, current_ts)
+                    created_trades += 1
+                except Exception as e:
+                    logger.error(f'failed to create trade: {e}')
 
             if save_balances:
                 self.maybe_save_balances(save_ts=current_ts)

{"golden_diff": "diff --git a/tools/data_faker/data_faker/actions.py b/tools/data_faker/data_faker/actions.py\n--- a/tools/data_faker/data_faker/actions.py\n+++ b/tools/data_faker/data_faker/actions.py\n@@ -79,7 +79,7 @@\n                 if asset in FIAT_CURRENCIES:\n                     self.rotki.data.db.add_manually_tracked_balances([ManuallyTrackedBalance(\n                         asset=asset,\n-                        label=f'{asset.identifier} balance',\n+                        label=f'{asset.identifier} balance {timestamp}',\n                         amount=value,\n                         location=Location.BANKS,\n                         tags=None,\n@@ -104,8 +104,11 @@\n             current_ts, save_balances, make_trade = self.get_next_ts()\n \n             if make_trade:\n-                self.create_action(created_trades, current_ts)\n-                created_trades += 1\n+                try:\n+                    self.create_action(created_trades, current_ts)\n+                    created_trades += 1\n+                except Exception as e:\n+                    logger.error(f'failed to create trade: {e}')\n \n             if save_balances:\n                 self.maybe_save_balances(save_ts=current_ts)\n", "issue": "Show all user trades/actions in a new page\n## Problem Definition\r\n\r\nWe have the user data action but users can't actually explore it in any way in the UI. That's not good and we should definitely offer a way to display all actions/trades in a dedicated page.\r\n\r\nThat would mean showing user's:\r\n- Exchange trades\r\n- Exchange deposit/withdrawals\r\n- DEX trades (when we support them)\r\n\r\n\r\n## Considerations\r\n\r\nNumber of visible actions will be limited whether user has premium or not. Need to think a bit here on how to make this a premium feature but also available to normal users. At the moment can only think of limit in the number of trades/actions shown.\r\n\r\n## Task\r\n\r\n## Backend\r\nThe backend already has an api call for this. It's actually being used with a filter for external location for the OTC trades.\r\n\r\n## Frontend\r\n\r\nThe frontend need a new dedicated page. User tactions or something like that. And there all trades, deposits/withdrawals of all locations should be displayed. This is going to be a very long list in most cases so we should think on optimizations and how to show the data in visually appealing and also useful way.\r\n\r\nI would imagine it would make sense to have different tables for trades, different for asset movements (deposit/withdrawals) etc. And perhaps a differen component per location. Or simply offer filtering.\r\n\n", "before_files": [{"content": "import logging\nimport random\nfrom typing import Tuple\n\nfrom rotkehlchen.assets.asset import Asset\nfrom rotkehlchen.balances.manual import ManuallyTrackedBalance\nfrom rotkehlchen.constants.assets import A_BTC, A_EUR, A_USD, FIAT_CURRENCIES\nfrom rotkehlchen.exchanges.data_structures import Trade, TradeType\nfrom rotkehlchen.fval import FVal\nfrom rotkehlchen.history import PriceHistorian\nfrom rotkehlchen.serialization.deserialize import deserialize_location, pair_get_assets\nfrom rotkehlchen.typing import Location, Timestamp, TradePair\n\nSTARTING_TIMESTAMP = 1464739200  # 01/06/2016\nNUMBER_OF_TRADES = 5\nSTARTING_FUNDS = {A_EUR: FVal(100000), A_BTC: FVal(10)}\n\nMAX_TRADE_DIFF_VARIANCE = 14400\n\nALLOWED_EXCHANGES = ['kraken', 'binance']\nKRAKEN_PAIRS = [TradePair('ETH_EUR'), TradePair('BTC_EUR')]\n\nMAX_TRADE_USD_VALUE = FVal(100)\nMAX_FEE_USD_VALUE = 1\n\n\nlogger = logging.getLogger(__name__)\n\n\nclass ActionWriter():\n\n    def __init__(\n            self,\n            trades_number: int,\n            seconds_between_trades: int,\n            seconds_between_balance_save: int,\n            rotkehlchen,\n            fake_kraken,\n            fake_binance,\n    ):\n        self.seconds_between_trades = seconds_between_trades\n        self.seconds_between_balance_save = seconds_between_balance_save\n        self.trades_number = trades_number\n        self.current_ts = STARTING_TIMESTAMP\n        self.last_trade_ts = 0\n        self.last_balance_save_ts = 0\n        self.funds = STARTING_FUNDS\n        self.rotki = rotkehlchen\n        self.kraken = fake_kraken\n        self.binance = fake_binance\n\n        timestamp, _, _ = self.get_next_ts()\n        for asset, value in self.funds.items():\n            if asset.is_fiat():\n                self.rotki.data.db.add_manually_tracked_balances([ManuallyTrackedBalance(\n                    asset=asset,\n                    label=f'{asset.identifier} balance',\n                    amount=value,\n                    location=Location.BANKS,\n                    tags=None,\n                )])\n        self.rotki.query_balances(requested_save_data=True, timestamp=timestamp)\n\n        # divide our starting funds between exchanges and keep a part out\n        divide_by = len(ALLOWED_EXCHANGES) + 1\n        for asset, value in self.funds.items():\n            amount = value / divide_by\n            for exchange in ALLOWED_EXCHANGES:\n                timestamp, _, _ = self.get_next_ts()\n\n                skip_exchange = asset in FIAT_CURRENCIES and exchange != 'kraken'\n\n                if not skip_exchange:\n                    getattr(self, exchange).deposit(\n                        asset=asset,\n                        amount=amount,\n                        time=timestamp,\n                    )\n                if asset in FIAT_CURRENCIES:\n                    self.rotki.data.db.add_manually_tracked_balances([ManuallyTrackedBalance(\n                        asset=asset,\n                        label=f'{asset.identifier} balance',\n                        amount=value,\n                        location=Location.BANKS,\n                        tags=None,\n                    )])\n\n        self.rotki.query_balances(requested_save_data=True, timestamp=timestamp)\n        self.last_balance_save_ts = timestamp\n\n    def maybe_save_balances(self, save_ts: Timestamp) -> None:\n        \"\"\"Maybe Save all current balances in the fake user's DB at the current timestamp\n\n        If the save_ts is not after the time we save balances then nothing happens\n        \"\"\"\n        if save_ts - self.last_balance_save_ts < self.seconds_between_balance_save:\n            return\n        self.rotki.query_balances(requested_save_data=True, timestamp=save_ts)\n        self.last_balance_save_ts = save_ts\n\n    def generate_history(self) -> None:\n        created_trades = 0\n        while created_trades <= self.trades_number:\n            current_ts, save_balances, make_trade = self.get_next_ts()\n\n            if make_trade:\n                self.create_action(created_trades, current_ts)\n                created_trades += 1\n\n            if save_balances:\n                self.maybe_save_balances(save_ts=current_ts)\n\n    @staticmethod\n    def query_historical_price(from_asset: Asset, to_asset: Asset, timestamp: Timestamp):\n        return PriceHistorian().query_historical_price(\n            from_asset=from_asset,\n            to_asset=to_asset,\n            timestamp=timestamp,\n        )\n\n    def increase_asset(self, asset: Asset, amount: FVal, exchange: str) -> None:\n        if asset not in self.funds:\n            self.funds[asset] = amount\n        else:\n            self.funds[asset] += amount\n\n        getattr(self, exchange).increase_asset(asset, amount)\n\n    def decrease_asset(self, asset: Asset, amount: FVal, exchange: str) -> None:\n        assert asset in self.funds, 'Asset should exist in funds'\n        assert amount <= self.funds[asset], 'We should have enough funds to decrease asset'\n        self.funds[asset] -= amount\n\n        getattr(self, exchange).decrease_asset(asset, amount)\n\n    def get_next_ts(self) -> Tuple[Timestamp, bool, bool]:\n        current_ts = self.current_ts\n        advance_by_secs = min(self.seconds_between_trades, self.seconds_between_balance_save)\n        secs_in_future = random.randint(\n            advance_by_secs,\n            advance_by_secs + MAX_TRADE_DIFF_VARIANCE,\n        )\n        self.current_ts += secs_in_future\n\n        save_balances = False\n        if self.current_ts - self.last_balance_save_ts >= self.seconds_between_balance_save:\n            save_balances = True\n\n        make_trade = False\n        if self.current_ts - self.last_trade_ts >= self.seconds_between_trades:\n            make_trade = True\n\n        return Timestamp(current_ts), save_balances, make_trade\n\n    def create_action(self, index: int, ts: Timestamp):\n        \"\"\"Create a random trade action on a random exchange depending\n        on the funds that are available in that exchange\"\"\"\n        # choose an exchange at random\n        exchange_name = random.choice(ALLOWED_EXCHANGES)\n        exchange = getattr(self, exchange_name)\n        # choose a random pair at that exchange\n        pair = exchange.choose_pair(\n            timestamp=ts,\n            price_query=self.query_historical_price,\n        )\n        print(\n            f'Creating trade {index + 1} / {self.trades_number} in {exchange_name}'\n            f' for the pair: {pair} at timestamp {ts}',\n        )\n        # depending on our funds decide on what to do. Buy/sell\n        base, quote = pair_get_assets(pair)\n        if exchange.get_balance(base) is None:\n            action_type = TradeType.BUY\n        elif exchange.get_balance(quote) is None:\n            action_type = TradeType.SELL\n        else:\n            # TODO: trade the one we have most of\n            action_type = random.choice(list(TradeType))\n\n        # if we are buying we are going to spend from the quote asset\n        if action_type == TradeType.BUY:\n            spending_asset = quote\n        else:  # selling spends from the base asset\n            spending_asset = base\n        # get a spending asset amount within our per-trade equivalent range and\n        # our available funds\n        spending_usd_rate = self.query_historical_price(spending_asset, A_USD, ts)\n        max_usd_in_spending_asset = spending_usd_rate * exchange.get_balance(spending_asset)\n        max_usd_equivalent_to_spend = min(max_usd_in_spending_asset, MAX_TRADE_USD_VALUE)\n        rate = self.query_historical_price(base, quote, ts)\n        usd_to_spend = FVal(random.uniform(0.01, float(max_usd_equivalent_to_spend)))\n        amount_in_spending_asset = usd_to_spend / spending_usd_rate\n        # if we are buying then the amount is the amount of asset we bought\n        if action_type == TradeType.BUY:\n            amount = amount_in_spending_asset / rate\n        # if we are selling the amount is the spending asset amount\n        else:\n            amount = amount_in_spending_asset\n\n        quote_asset_usd_rate = self.query_historical_price(quote, A_USD, ts)\n        fee_in_quote_currency = FVal(random.uniform(0, MAX_FEE_USD_VALUE)) / quote_asset_usd_rate\n\n        # create the trade\n        trade = Trade(\n            timestamp=ts,\n            location=deserialize_location(exchange_name),\n            pair=pair,\n            trade_type=action_type,\n            amount=amount,\n            rate=rate,\n            fee=fee_in_quote_currency,\n            fee_currency=quote,\n            link='',\n            notes='',\n        )\n        logger.info(f'Created trade: {trade}')\n\n        # Adjust our global and per exchange accounting\n        if action_type == TradeType.BUY:\n            # we buy so we increase our base asset by amount\n            self.increase_asset(base, amount, exchange_name)\n            # and decrease quote by amount * rate\n            self.decrease_asset(quote, amount * rate, exchange_name)\n        else:\n            # we sell so we increase our quote asset\n            self.increase_asset(quote, amount * rate, exchange_name)\n            # and decrease our base asset\n            self.decrease_asset(base, amount, exchange_name)\n\n        # finally add it to the exchange\n        exchange.append_trade(trade)\n", "path": "tools/data_faker/data_faker/actions.py"}], "after_files": [{"content": "import logging\nimport random\nfrom typing import Tuple\n\nfrom rotkehlchen.assets.asset import Asset\nfrom rotkehlchen.balances.manual import ManuallyTrackedBalance\nfrom rotkehlchen.constants.assets import A_BTC, A_EUR, A_USD, FIAT_CURRENCIES\nfrom rotkehlchen.exchanges.data_structures import Trade, TradeType\nfrom rotkehlchen.fval import FVal\nfrom rotkehlchen.history import PriceHistorian\nfrom rotkehlchen.serialization.deserialize import deserialize_location, pair_get_assets\nfrom rotkehlchen.typing import Location, Timestamp, TradePair\n\nSTARTING_TIMESTAMP = 1464739200  # 01/06/2016\nNUMBER_OF_TRADES = 5\nSTARTING_FUNDS = {A_EUR: FVal(100000), A_BTC: FVal(10)}\n\nMAX_TRADE_DIFF_VARIANCE = 14400\n\nALLOWED_EXCHANGES = ['kraken', 'binance']\nKRAKEN_PAIRS = [TradePair('ETH_EUR'), TradePair('BTC_EUR')]\n\nMAX_TRADE_USD_VALUE = FVal(100)\nMAX_FEE_USD_VALUE = 1\n\n\nlogger = logging.getLogger(__name__)\n\n\nclass ActionWriter():\n\n    def __init__(\n            self,\n            trades_number: int,\n            seconds_between_trades: int,\n            seconds_between_balance_save: int,\n            rotkehlchen,\n            fake_kraken,\n            fake_binance,\n    ):\n        self.seconds_between_trades = seconds_between_trades\n        self.seconds_between_balance_save = seconds_between_balance_save\n        self.trades_number = trades_number\n        self.current_ts = STARTING_TIMESTAMP\n        self.last_trade_ts = 0\n        self.last_balance_save_ts = 0\n        self.funds = STARTING_FUNDS\n        self.rotki = rotkehlchen\n        self.kraken = fake_kraken\n        self.binance = fake_binance\n\n        timestamp, _, _ = self.get_next_ts()\n        for asset, value in self.funds.items():\n            if asset.is_fiat():\n                self.rotki.data.db.add_manually_tracked_balances([ManuallyTrackedBalance(\n                    asset=asset,\n                    label=f'{asset.identifier} balance',\n                    amount=value,\n                    location=Location.BANKS,\n                    tags=None,\n                )])\n        self.rotki.query_balances(requested_save_data=True, timestamp=timestamp)\n\n        # divide our starting funds between exchanges and keep a part out\n        divide_by = len(ALLOWED_EXCHANGES) + 1\n        for asset, value in self.funds.items():\n            amount = value / divide_by\n            for exchange in ALLOWED_EXCHANGES:\n                timestamp, _, _ = self.get_next_ts()\n\n                skip_exchange = asset in FIAT_CURRENCIES and exchange != 'kraken'\n\n                if not skip_exchange:\n                    getattr(self, exchange).deposit(\n                        asset=asset,\n                        amount=amount,\n                        time=timestamp,\n                    )\n                if asset in FIAT_CURRENCIES:\n                    self.rotki.data.db.add_manually_tracked_balances([ManuallyTrackedBalance(\n                        asset=asset,\n                        label=f'{asset.identifier} balance {timestamp}',\n                        amount=value,\n                        location=Location.BANKS,\n                        tags=None,\n                    )])\n\n        self.rotki.query_balances(requested_save_data=True, timestamp=timestamp)\n        self.last_balance_save_ts = timestamp\n\n    def maybe_save_balances(self, save_ts: Timestamp) -> None:\n        \"\"\"Maybe Save all current balances in the fake user's DB at the current timestamp\n\n        If the save_ts is not after the time we save balances then nothing happens\n        \"\"\"\n        if save_ts - self.last_balance_save_ts < self.seconds_between_balance_save:\n            return\n        self.rotki.query_balances(requested_save_data=True, timestamp=save_ts)\n        self.last_balance_save_ts = save_ts\n\n    def generate_history(self) -> None:\n        created_trades = 0\n        while created_trades <= self.trades_number:\n            current_ts, save_balances, make_trade = self.get_next_ts()\n\n            if make_trade:\n                try:\n                    self.create_action(created_trades, current_ts)\n                    created_trades += 1\n                except Exception as e:\n                    logger.error(f'failed to create trade: {e}')\n\n            if save_balances:\n                self.maybe_save_balances(save_ts=current_ts)\n\n    @staticmethod\n    def query_historical_price(from_asset: Asset, to_asset: Asset, timestamp: Timestamp):\n        return PriceHistorian().query_historical_price(\n            from_asset=from_asset,\n            to_asset=to_asset,\n            timestamp=timestamp,\n        )\n\n    def increase_asset(self, asset: Asset, amount: FVal, exchange: str) -> None:\n        if asset not in self.funds:\n            self.funds[asset] = amount\n        else:\n            self.funds[asset] += amount\n\n        getattr(self, exchange).increase_asset(asset, amount)\n\n    def decrease_asset(self, asset: Asset, amount: FVal, exchange: str) -> None:\n        assert asset in self.funds, 'Asset should exist in funds'\n        assert amount <= self.funds[asset], 'We should have enough funds to decrease asset'\n        self.funds[asset] -= amount\n\n        getattr(self, exchange).decrease_asset(asset, amount)\n\n    def get_next_ts(self) -> Tuple[Timestamp, bool, bool]:\n        current_ts = self.current_ts\n        advance_by_secs = min(self.seconds_between_trades, self.seconds_between_balance_save)\n        secs_in_future = random.randint(\n            advance_by_secs,\n            advance_by_secs + MAX_TRADE_DIFF_VARIANCE,\n        )\n        self.current_ts += secs_in_future\n\n        save_balances = False\n        if self.current_ts - self.last_balance_save_ts >= self.seconds_between_balance_save:\n            save_balances = True\n\n        make_trade = False\n        if self.current_ts - self.last_trade_ts >= self.seconds_between_trades:\n            make_trade = True\n\n        return Timestamp(current_ts), save_balances, make_trade\n\n    def create_action(self, index: int, ts: Timestamp):\n        \"\"\"Create a random trade action on a random exchange depending\n        on the funds that are available in that exchange\"\"\"\n        # choose an exchange at random\n        exchange_name = random.choice(ALLOWED_EXCHANGES)\n        exchange = getattr(self, exchange_name)\n        # choose a random pair at that exchange\n        pair = exchange.choose_pair(\n            timestamp=ts,\n            price_query=self.query_historical_price,\n        )\n        print(\n            f'Creating trade {index + 1} / {self.trades_number} in {exchange_name}'\n            f' for the pair: {pair} at timestamp {ts}',\n        )\n        # depending on our funds decide on what to do. Buy/sell\n        base, quote = pair_get_assets(pair)\n        if exchange.get_balance(base) is None:\n            action_type = TradeType.BUY\n        elif exchange.get_balance(quote) is None:\n            action_type = TradeType.SELL\n        else:\n            # TODO: trade the one we have most of\n            action_type = random.choice(list(TradeType))\n\n        # if we are buying we are going to spend from the quote asset\n        if action_type == TradeType.BUY:\n            spending_asset = quote\n        else:  # selling spends from the base asset\n            spending_asset = base\n        # get a spending asset amount within our per-trade equivalent range and\n        # our available funds\n        spending_usd_rate = self.query_historical_price(spending_asset, A_USD, ts)\n        max_usd_in_spending_asset = spending_usd_rate * exchange.get_balance(spending_asset)\n        max_usd_equivalent_to_spend = min(max_usd_in_spending_asset, MAX_TRADE_USD_VALUE)\n        rate = self.query_historical_price(base, quote, ts)\n        usd_to_spend = FVal(random.uniform(0.01, float(max_usd_equivalent_to_spend)))\n        amount_in_spending_asset = usd_to_spend / spending_usd_rate\n        # if we are buying then the amount is the amount of asset we bought\n        if action_type == TradeType.BUY:\n            amount = amount_in_spending_asset / rate\n        # if we are selling the amount is the spending asset amount\n        else:\n            amount = amount_in_spending_asset\n\n        quote_asset_usd_rate = self.query_historical_price(quote, A_USD, ts)\n        fee_in_quote_currency = FVal(random.uniform(0, MAX_FEE_USD_VALUE)) / quote_asset_usd_rate\n\n        # create the trade\n        trade = Trade(\n            timestamp=ts,\n            location=deserialize_location(exchange_name),\n            pair=pair,\n            trade_type=action_type,\n            amount=amount,\n            rate=rate,\n            fee=fee_in_quote_currency,\n            fee_currency=quote,\n            link='',\n            notes='',\n        )\n        logger.info(f'Created trade: {trade}')\n\n        # Adjust our global and per exchange accounting\n        if action_type == TradeType.BUY:\n            # we buy so we increase our base asset by amount\n            self.increase_asset(base, amount, exchange_name)\n            # and decrease quote by amount * rate\n            self.decrease_asset(quote, amount * rate, exchange_name)\n        else:\n            # we sell so we increase our quote asset\n            self.increase_asset(quote, amount * rate, exchange_name)\n            # and decrease our base asset\n            self.decrease_asset(base, amount, exchange_name)\n\n        # finally add it to the exchange\n        exchange.append_trade(trade)\n", "path": "tools/data_faker/data_faker/actions.py"}]}