We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
MIN_TRADE_REQUIREMENT_NOT_MET
"stake_amount": 0.0005

Traceback (most recent call last):
  File "./freqtrade/main.py", line 70, in _process
    trade = create_trade(float(_CONF['stake_amount']))
  File "./freqtrade/main.py", line 213, in create_trade
    order_id = exchange.buy(pair, buy_limit, amount)
  File "/vagrant/freqtrade/freqtrade/exchange/__init__.py", line 100, in buy
    return _API.buy(pair, rate, amount)
  File "/vagrant/freqtrade/freqtrade/exchange/bittrex.py", line 53, in buy
    amount=amount))
RuntimeError: MIN_TRADE_REQUIREMENT_NOT_MET params=(BTC_CLUB, 0.00067, 0.7444029850746269)

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `freqtrade/main.py`
Content:
```
1 #!/usr/bin/env python3
2 import copy
3 import json
4 import logging
5 import sys
6 import time
7 import traceback
8 from datetime import datetime
9 from typing import Dict, Optional, List
10 
11 import requests
12 from cachetools import cached, TTLCache
13 
14 from freqtrade import __version__, exchange, persistence, rpc, DependencyException, \
15     OperationalException
16 from freqtrade.analyze import get_signal, SignalType
17 from freqtrade.misc import State, get_state, update_state, parse_args, throttle, \
18     load_config
19 from freqtrade.persistence import Trade
20 
21 logger = logging.getLogger('freqtrade')
22 
23 _CONF = {}
24 
25 
26 def refresh_whitelist(whitelist: Optional[List[str]] = None) -> None:
27     """
28     Check wallet health and remove pair from whitelist if necessary
29     :param whitelist: a new whitelist (optional)
30     :return: None
31     """
32     whitelist = whitelist or _CONF['exchange']['pair_whitelist']
33 
34     sanitized_whitelist = []
35     health = exchange.get_wallet_health()
36     for status in health:
37         pair = '{}_{}'.format(_CONF['stake_currency'], status['Currency'])
38         if pair not in whitelist:
39             continue
40         if status['IsActive']:
41             sanitized_whitelist.append(pair)
42         else:
43             logger.info(
44                 'Ignoring %s from whitelist (reason: %s).',
45                 pair, status.get('Notice') or 'wallet is not active'
46             )
47     if _CONF['exchange']['pair_whitelist'] != sanitized_whitelist:
48         logger.debug('Using refreshed pair whitelist: %s ...', sanitized_whitelist)
49         _CONF['exchange']['pair_whitelist'] = sanitized_whitelist
50 
51 
52 def _process(dynamic_whitelist: Optional[bool] = False) -> bool:
53     """
54     Queries the persistence layer for open trades and handles them,
55     otherwise a new trade is created.
56     :param: dynamic_whitelist: True is a dynamic whitelist should be generated (optional)
57     :return: True if a trade has been created or closed, False otherwise
58     """
59     state_changed = False
60     try:
61         # Refresh whitelist based on wallet maintenance
62         refresh_whitelist(
63             gen_pair_whitelist(_CONF['stake_currency']) if dynamic_whitelist else None
64         )
65         # Query trades from persistence layer
66         trades = Trade.query.filter(Trade.is_open.is_(True)).all()
67         if len(trades) < _CONF['max_open_trades']:
68             try:
69                 # Create entity and execute trade
70                 state_changed = create_trade(float(_CONF['stake_amount']))
71                 if not state_changed:
72                     logger.info(
73                         'Checked all whitelisted currencies. '
74                         'Found no suitable entry positions for buying. Will keep looking ...'
75                     )
76             except DependencyException as e:
77                 logger.warning('Unable to create trade: %s', e)
78 
79         for trade in trades:
80             # Get order details for actual price per unit
81             if trade.open_order_id:
82                 # Update trade with order values
83                 logger.info('Got open order for %s', trade)
84                 trade.update(exchange.get_order(trade.open_order_id))
85 
86             if trade.is_open and trade.open_order_id is None:
87                 # Check if we can sell our current pair
88                 state_changed = handle_trade(trade) or state_changed
89 
90             Trade.session.flush()
91     except (requests.exceptions.RequestException, json.JSONDecodeError) as error:
92         logger.warning(
93             'Got %s in _process(), retrying in 30 seconds...',
94             error
95         )
96         time.sleep(30)
97     except OperationalException:
98         rpc.send_msg('*Status:* Got OperationalException:\n```\n{traceback}```{hint}'.format(
99             traceback=traceback.format_exc(),
100             hint='Issue `/start` if you think it is safe to restart.'
101         ))
102         logger.exception('Got OperationalException. Stopping trader ...')
103         update_state(State.STOPPED)
104     return state_changed
105 
106 
107 def execute_sell(trade: Trade, limit: float) -> None:
108     """
109     Executes a limit sell for the given trade and limit
110     :param trade: Trade instance
111     :param limit: limit rate for the sell order
112     :return: None
113     """
114     # Execute sell and update trade record
115     order_id = exchange.sell(str(trade.pair), limit, trade.amount)
116     trade.open_order_id = order_id
117 
118     fmt_exp_profit = round(trade.calc_profit(limit) * 100, 2)
119     rpc.send_msg('*{}:* Selling [{}]({}) with limit `{:.8f} (profit: ~{:.2f}%)`'.format(
120         trade.exchange,
121         trade.pair.replace('_', '/'),
122         exchange.get_pair_detail_url(trade.pair),
123         limit,
124         fmt_exp_profit
125     ))
126     Trade.session.flush()
127 
128 
129 def min_roi_reached(trade: Trade, current_rate: float, current_time: datetime) -> bool:
130     """
131     Based an earlier trade and current price and ROI configuration, decides whether bot should sell
132     :return True if bot should sell at current rate
133     """
134     current_profit = trade.calc_profit(current_rate)
135     if 'stoploss' in _CONF and current_profit < float(_CONF['stoploss']):
136         logger.debug('Stop loss hit.')
137         return True
138 
139     # Check if time matches and current rate is above threshold
140     time_diff = (current_time - trade.open_date).total_seconds() / 60
141     for duration, threshold in sorted(_CONF['minimal_roi'].items()):
142         if time_diff > float(duration) and current_profit > threshold:
143             return True
144 
145     logger.debug('Threshold not reached. (cur_profit: %1.2f%%)', current_profit * 100.0)
146     return False
147 
148 
149 def handle_trade(trade: Trade) -> bool:
150     """
151     Sells the current pair if the threshold is reached and updates the trade record.
152     :return: True if trade has been sold, False otherwise
153     """
154     if not trade.is_open:
155         raise ValueError('attempt to handle closed trade: {}'.format(trade))
156 
157     logger.debug('Handling %s ...', trade)
158     current_rate = exchange.get_ticker(trade.pair)['bid']
159     if min_roi_reached(trade, current_rate, datetime.utcnow()) or get_signal(trade.pair, SignalType.SELL):
160         execute_sell(trade, current_rate)
161         return True
162     return False
163 
164 
165 def get_target_bid(ticker: Dict[str, float]) -> float:
166     """ Calculates bid target between current ask price and last price """
167     if ticker['ask'] < ticker['last']:
168         return ticker['ask']
169     balance = _CONF['bid_strategy']['ask_last_balance']
170     return ticker['ask'] + balance * (ticker['last'] - ticker['ask'])
171 
172 
173 def create_trade(stake_amount: float) -> bool:
174     """
175     Checks the implemented trading indicator(s) for a randomly picked pair,
176     if one pair triggers the buy_signal a new trade record gets created
177     :param stake_amount: amount of btc to spend
178     :return: True if a trade object has been created and persisted, False otherwise
179     """
180     logger.info(
181         'Checking buy signals to create a new trade with stake_amount: %f ...',
182         stake_amount
183     )
184     whitelist = copy.deepcopy(_CONF['exchange']['pair_whitelist'])
185     # Check if stake_amount is fulfilled
186     if exchange.get_balance(_CONF['stake_currency']) < stake_amount:
187         raise DependencyException(
188             'stake amount is not fulfilled (currency={})'.format(_CONF['stake_currency'])
189         )
190 
191     # Remove currently opened and latest pairs from whitelist
192     for trade in Trade.query.filter(Trade.is_open.is_(True)).all():
193         if trade.pair in whitelist:
194             whitelist.remove(trade.pair)
195             logger.debug('Ignoring %s in pair whitelist', trade.pair)
196     if not whitelist:
197         raise DependencyException('No pair in whitelist')
198 
199     # Pick pair based on StochRSI buy signals
200     for _pair in whitelist:
201         if get_signal(_pair, SignalType.BUY):
202             pair = _pair
203             break
204     else:
205         return False
206 
207     # Calculate amount and subtract fee
208     fee = exchange.get_fee()
209     buy_limit = get_target_bid(exchange.get_ticker(pair))
210     amount = (1 - fee) * stake_amount / buy_limit
211 
212     order_id = exchange.buy(pair, buy_limit, amount)
213     # Create trade entity and return
214     rpc.send_msg('*{}:* Buying [{}]({}) with limit `{:.8f}`'.format(
215         exchange.get_name().upper(),
216         pair.replace('_', '/'),
217         exchange.get_pair_detail_url(pair),
218         buy_limit
219     ))
220     # Fee is applied twice because we make a LIMIT_BUY and LIMIT_SELL
221     trade = Trade(
222         pair=pair,
223         stake_amount=stake_amount,
224         amount=amount,
225         fee=fee * 2,
226         open_rate=buy_limit,
227         open_date=datetime.utcnow(),
228         exchange=exchange.get_name().upper(),
229         open_order_id=order_id
230     )
231     Trade.session.add(trade)
232     Trade.session.flush()
233     return True
234 
235 
236 def init(config: dict, db_url: Optional[str] = None) -> None:
237     """
238     Initializes all modules and updates the config
239     :param config: config as dict
240     :param db_url: database connector string for sqlalchemy (Optional)
241     :return: None
242     """
243     # Initialize all modules
244     rpc.init(config)
245     persistence.init(config, db_url)
246     exchange.init(config)
247 
248     # Set initial application state
249     initial_state = config.get('initial_state')
250     if initial_state:
251         update_state(State[initial_state.upper()])
252     else:
253         update_state(State.STOPPED)
254 
255 
256 @cached(TTLCache(maxsize=1, ttl=1800))
257 def gen_pair_whitelist(base_currency: str, topn: int = 20, key: str = 'BaseVolume') -> List[str]:
258     """
259     Updates the whitelist with with a dynamically generated list
260     :param base_currency: base currency as str
261     :param topn: maximum number of returned results
262     :param key: sort key (defaults to 'BaseVolume')
263     :return: List of pairs
264     """
265     summaries = sorted(
266         (s for s in exchange.get_market_summaries() if s['MarketName'].startswith(base_currency)),
267         key=lambda s: s.get(key) or 0.0,
268         reverse=True
269     )
270     return [s['MarketName'].replace('-', '_') for s in summaries[:topn]]
271 
272 
273 def cleanup() -> None:
274     """
275     Cleanup the application state und finish all pending tasks
276     :return: None
277     """
278     rpc.send_msg('*Status:* `Stopping trader...`')
279     logger.info('Stopping trader and cleaning up modules...')
280     update_state(State.STOPPED)
281     persistence.cleanup()
282     rpc.cleanup()
283     exit(0)
284 
285 
286 def main() -> None:
287     """
288     Loads and validates the config and handles the main loop
289     :return: None
290     """
291     global _CONF
292     args = parse_args(sys.argv[1:])
293     if not args:
294         exit(0)
295 
296     # Initialize logger
297     logging.basicConfig(
298         level=args.loglevel,
299         format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
300     )
301 
302     logger.info(
303         'Starting freqtrade %s (loglevel=%s)',
304         __version__,
305         logging.getLevelName(args.loglevel)
306     )
307 
308     # Load and validate configuration
309     _CONF = load_config(args.config)
310 
311     # Initialize all modules and start main loop
312     if args.dynamic_whitelist:
313         logger.info('Using dynamically generated whitelist. (--dynamic-whitelist detected)')
314 
315     try:
316         init(_CONF)
317         old_state = None
318         while True:
319             new_state = get_state()
320             # Log state transition
321             if new_state != old_state:
322                 rpc.send_msg('*Status:* `{}`'.format(new_state.name.lower()))
323                 logger.info('Changing state to: %s', new_state.name)
324 
325             if new_state == State.STOPPED:
326                 time.sleep(1)
327             elif new_state == State.RUNNING:
328                 throttle(
329                     _process,
330                     min_secs=_CONF['internals'].get('process_throttle_secs', 10),
331                     dynamic_whitelist=args.dynamic_whitelist,
332                 )
333             old_state = new_state
334     except KeyboardInterrupt:
335         logger.info('Got SIGINT, aborting ...')
336     except BaseException:
337         logger.exception('Got fatal exception!')
338     finally:
339         cleanup()
340 
341 
342 if __name__ == '__main__':
343     main()
344
```
--- 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/freqtrade/main.py b/freqtrade/main.py
--- a/freqtrade/main.py
+++ b/freqtrade/main.py
@@ -204,10 +204,9 @@
     else:
         return False
 
-    # Calculate amount and subtract fee
-    fee = exchange.get_fee()
+    # Calculate amount
     buy_limit = get_target_bid(exchange.get_ticker(pair))
-    amount = (1 - fee) * stake_amount / buy_limit
+    amount = stake_amount / buy_limit
 
     order_id = exchange.buy(pair, buy_limit, amount)
     # Create trade entity and return
@@ -222,7 +221,7 @@
         pair=pair,
         stake_amount=stake_amount,
         amount=amount,
-        fee=fee * 2,
+        fee=exchange.get_fee() * 2,
         open_rate=buy_limit,
         open_date=datetime.utcnow(),
         exchange=exchange.get_name().upper(),

{"golden_diff": "diff --git a/freqtrade/main.py b/freqtrade/main.py\n--- a/freqtrade/main.py\n+++ b/freqtrade/main.py\n@@ -204,10 +204,9 @@\n     else:\n         return False\n \n-    # Calculate amount and subtract fee\n-    fee = exchange.get_fee()\n+    # Calculate amount\n     buy_limit = get_target_bid(exchange.get_ticker(pair))\n-    amount = (1 - fee) * stake_amount / buy_limit\n+    amount = stake_amount / buy_limit\n \n     order_id = exchange.buy(pair, buy_limit, amount)\n     # Create trade entity and return\n@@ -222,7 +221,7 @@\n         pair=pair,\n         stake_amount=stake_amount,\n         amount=amount,\n-        fee=fee * 2,\n+        fee=exchange.get_fee() * 2,\n         open_rate=buy_limit,\n         open_date=datetime.utcnow(),\n         exchange=exchange.get_name().upper(),\n", "issue": "MIN_TRADE_REQUIREMENT_NOT_MET\n\"stake_amount\": 0.0005\r\n\r\nTraceback (most recent call last):\r\n  File \"./freqtrade/main.py\", line 70, in _process\r\n    trade = create_trade(float(_CONF['stake_amount']))\r\n  File \"./freqtrade/main.py\", line 213, in create_trade\r\n    order_id = exchange.buy(pair, buy_limit, amount)\r\n  File \"/vagrant/freqtrade/freqtrade/exchange/__init__.py\", line 100, in buy\r\n    return _API.buy(pair, rate, amount)\r\n  File \"/vagrant/freqtrade/freqtrade/exchange/bittrex.py\", line 53, in buy\r\n    amount=amount))\r\nRuntimeError: MIN_TRADE_REQUIREMENT_NOT_MET params=(BTC_CLUB, 0.00067, 0.7444029850746269)\n", "before_files": [{"content": "#!/usr/bin/env python3\nimport copy\nimport json\nimport logging\nimport sys\nimport time\nimport traceback\nfrom datetime import datetime\nfrom typing import Dict, Optional, List\n\nimport requests\nfrom cachetools import cached, TTLCache\n\nfrom freqtrade import __version__, exchange, persistence, rpc, DependencyException, \\\n    OperationalException\nfrom freqtrade.analyze import get_signal, SignalType\nfrom freqtrade.misc import State, get_state, update_state, parse_args, throttle, \\\n    load_config\nfrom freqtrade.persistence import Trade\n\nlogger = logging.getLogger('freqtrade')\n\n_CONF = {}\n\n\ndef refresh_whitelist(whitelist: Optional[List[str]] = None) -> None:\n    \"\"\"\n    Check wallet health and remove pair from whitelist if necessary\n    :param whitelist: a new whitelist (optional)\n    :return: None\n    \"\"\"\n    whitelist = whitelist or _CONF['exchange']['pair_whitelist']\n\n    sanitized_whitelist = []\n    health = exchange.get_wallet_health()\n    for status in health:\n        pair = '{}_{}'.format(_CONF['stake_currency'], status['Currency'])\n        if pair not in whitelist:\n            continue\n        if status['IsActive']:\n            sanitized_whitelist.append(pair)\n        else:\n            logger.info(\n                'Ignoring %s from whitelist (reason: %s).',\n                pair, status.get('Notice') or 'wallet is not active'\n            )\n    if _CONF['exchange']['pair_whitelist'] != sanitized_whitelist:\n        logger.debug('Using refreshed pair whitelist: %s ...', sanitized_whitelist)\n        _CONF['exchange']['pair_whitelist'] = sanitized_whitelist\n\n\ndef _process(dynamic_whitelist: Optional[bool] = False) -> bool:\n    \"\"\"\n    Queries the persistence layer for open trades and handles them,\n    otherwise a new trade is created.\n    :param: dynamic_whitelist: True is a dynamic whitelist should be generated (optional)\n    :return: True if a trade has been created or closed, False otherwise\n    \"\"\"\n    state_changed = False\n    try:\n        # Refresh whitelist based on wallet maintenance\n        refresh_whitelist(\n            gen_pair_whitelist(_CONF['stake_currency']) if dynamic_whitelist else None\n        )\n        # Query trades from persistence layer\n        trades = Trade.query.filter(Trade.is_open.is_(True)).all()\n        if len(trades) < _CONF['max_open_trades']:\n            try:\n                # Create entity and execute trade\n                state_changed = create_trade(float(_CONF['stake_amount']))\n                if not state_changed:\n                    logger.info(\n                        'Checked all whitelisted currencies. '\n                        'Found no suitable entry positions for buying. Will keep looking ...'\n                    )\n            except DependencyException as e:\n                logger.warning('Unable to create trade: %s', e)\n\n        for trade in trades:\n            # Get order details for actual price per unit\n            if trade.open_order_id:\n                # Update trade with order values\n                logger.info('Got open order for %s', trade)\n                trade.update(exchange.get_order(trade.open_order_id))\n\n            if trade.is_open and trade.open_order_id is None:\n                # Check if we can sell our current pair\n                state_changed = handle_trade(trade) or state_changed\n\n            Trade.session.flush()\n    except (requests.exceptions.RequestException, json.JSONDecodeError) as error:\n        logger.warning(\n            'Got %s in _process(), retrying in 30 seconds...',\n            error\n        )\n        time.sleep(30)\n    except OperationalException:\n        rpc.send_msg('*Status:* Got OperationalException:\\n```\\n{traceback}```{hint}'.format(\n            traceback=traceback.format_exc(),\n            hint='Issue `/start` if you think it is safe to restart.'\n        ))\n        logger.exception('Got OperationalException. Stopping trader ...')\n        update_state(State.STOPPED)\n    return state_changed\n\n\ndef execute_sell(trade: Trade, limit: float) -> None:\n    \"\"\"\n    Executes a limit sell for the given trade and limit\n    :param trade: Trade instance\n    :param limit: limit rate for the sell order\n    :return: None\n    \"\"\"\n    # Execute sell and update trade record\n    order_id = exchange.sell(str(trade.pair), limit, trade.amount)\n    trade.open_order_id = order_id\n\n    fmt_exp_profit = round(trade.calc_profit(limit) * 100, 2)\n    rpc.send_msg('*{}:* Selling [{}]({}) with limit `{:.8f} (profit: ~{:.2f}%)`'.format(\n        trade.exchange,\n        trade.pair.replace('_', '/'),\n        exchange.get_pair_detail_url(trade.pair),\n        limit,\n        fmt_exp_profit\n    ))\n    Trade.session.flush()\n\n\ndef min_roi_reached(trade: Trade, current_rate: float, current_time: datetime) -> bool:\n    \"\"\"\n    Based an earlier trade and current price and ROI configuration, decides whether bot should sell\n    :return True if bot should sell at current rate\n    \"\"\"\n    current_profit = trade.calc_profit(current_rate)\n    if 'stoploss' in _CONF and current_profit < float(_CONF['stoploss']):\n        logger.debug('Stop loss hit.')\n        return True\n\n    # Check if time matches and current rate is above threshold\n    time_diff = (current_time - trade.open_date).total_seconds() / 60\n    for duration, threshold in sorted(_CONF['minimal_roi'].items()):\n        if time_diff > float(duration) and current_profit > threshold:\n            return True\n\n    logger.debug('Threshold not reached. (cur_profit: %1.2f%%)', current_profit * 100.0)\n    return False\n\n\ndef handle_trade(trade: Trade) -> bool:\n    \"\"\"\n    Sells the current pair if the threshold is reached and updates the trade record.\n    :return: True if trade has been sold, False otherwise\n    \"\"\"\n    if not trade.is_open:\n        raise ValueError('attempt to handle closed trade: {}'.format(trade))\n\n    logger.debug('Handling %s ...', trade)\n    current_rate = exchange.get_ticker(trade.pair)['bid']\n    if min_roi_reached(trade, current_rate, datetime.utcnow()) or get_signal(trade.pair, SignalType.SELL):\n        execute_sell(trade, current_rate)\n        return True\n    return False\n\n\ndef get_target_bid(ticker: Dict[str, float]) -> float:\n    \"\"\" Calculates bid target between current ask price and last price \"\"\"\n    if ticker['ask'] < ticker['last']:\n        return ticker['ask']\n    balance = _CONF['bid_strategy']['ask_last_balance']\n    return ticker['ask'] + balance * (ticker['last'] - ticker['ask'])\n\n\ndef create_trade(stake_amount: float) -> bool:\n    \"\"\"\n    Checks the implemented trading indicator(s) for a randomly picked pair,\n    if one pair triggers the buy_signal a new trade record gets created\n    :param stake_amount: amount of btc to spend\n    :return: True if a trade object has been created and persisted, False otherwise\n    \"\"\"\n    logger.info(\n        'Checking buy signals to create a new trade with stake_amount: %f ...',\n        stake_amount\n    )\n    whitelist = copy.deepcopy(_CONF['exchange']['pair_whitelist'])\n    # Check if stake_amount is fulfilled\n    if exchange.get_balance(_CONF['stake_currency']) < stake_amount:\n        raise DependencyException(\n            'stake amount is not fulfilled (currency={})'.format(_CONF['stake_currency'])\n        )\n\n    # Remove currently opened and latest pairs from whitelist\n    for trade in Trade.query.filter(Trade.is_open.is_(True)).all():\n        if trade.pair in whitelist:\n            whitelist.remove(trade.pair)\n            logger.debug('Ignoring %s in pair whitelist', trade.pair)\n    if not whitelist:\n        raise DependencyException('No pair in whitelist')\n\n    # Pick pair based on StochRSI buy signals\n    for _pair in whitelist:\n        if get_signal(_pair, SignalType.BUY):\n            pair = _pair\n            break\n    else:\n        return False\n\n    # Calculate amount and subtract fee\n    fee = exchange.get_fee()\n    buy_limit = get_target_bid(exchange.get_ticker(pair))\n    amount = (1 - fee) * stake_amount / buy_limit\n\n    order_id = exchange.buy(pair, buy_limit, amount)\n    # Create trade entity and return\n    rpc.send_msg('*{}:* Buying [{}]({}) with limit `{:.8f}`'.format(\n        exchange.get_name().upper(),\n        pair.replace('_', '/'),\n        exchange.get_pair_detail_url(pair),\n        buy_limit\n    ))\n    # Fee is applied twice because we make a LIMIT_BUY and LIMIT_SELL\n    trade = Trade(\n        pair=pair,\n        stake_amount=stake_amount,\n        amount=amount,\n        fee=fee * 2,\n        open_rate=buy_limit,\n        open_date=datetime.utcnow(),\n        exchange=exchange.get_name().upper(),\n        open_order_id=order_id\n    )\n    Trade.session.add(trade)\n    Trade.session.flush()\n    return True\n\n\ndef init(config: dict, db_url: Optional[str] = None) -> None:\n    \"\"\"\n    Initializes all modules and updates the config\n    :param config: config as dict\n    :param db_url: database connector string for sqlalchemy (Optional)\n    :return: None\n    \"\"\"\n    # Initialize all modules\n    rpc.init(config)\n    persistence.init(config, db_url)\n    exchange.init(config)\n\n    # Set initial application state\n    initial_state = config.get('initial_state')\n    if initial_state:\n        update_state(State[initial_state.upper()])\n    else:\n        update_state(State.STOPPED)\n\n\n@cached(TTLCache(maxsize=1, ttl=1800))\ndef gen_pair_whitelist(base_currency: str, topn: int = 20, key: str = 'BaseVolume') -> List[str]:\n    \"\"\"\n    Updates the whitelist with with a dynamically generated list\n    :param base_currency: base currency as str\n    :param topn: maximum number of returned results\n    :param key: sort key (defaults to 'BaseVolume')\n    :return: List of pairs\n    \"\"\"\n    summaries = sorted(\n        (s for s in exchange.get_market_summaries() if s['MarketName'].startswith(base_currency)),\n        key=lambda s: s.get(key) or 0.0,\n        reverse=True\n    )\n    return [s['MarketName'].replace('-', '_') for s in summaries[:topn]]\n\n\ndef cleanup() -> None:\n    \"\"\"\n    Cleanup the application state und finish all pending tasks\n    :return: None\n    \"\"\"\n    rpc.send_msg('*Status:* `Stopping trader...`')\n    logger.info('Stopping trader and cleaning up modules...')\n    update_state(State.STOPPED)\n    persistence.cleanup()\n    rpc.cleanup()\n    exit(0)\n\n\ndef main() -> None:\n    \"\"\"\n    Loads and validates the config and handles the main loop\n    :return: None\n    \"\"\"\n    global _CONF\n    args = parse_args(sys.argv[1:])\n    if not args:\n        exit(0)\n\n    # Initialize logger\n    logging.basicConfig(\n        level=args.loglevel,\n        format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',\n    )\n\n    logger.info(\n        'Starting freqtrade %s (loglevel=%s)',\n        __version__,\n        logging.getLevelName(args.loglevel)\n    )\n\n    # Load and validate configuration\n    _CONF = load_config(args.config)\n\n    # Initialize all modules and start main loop\n    if args.dynamic_whitelist:\n        logger.info('Using dynamically generated whitelist. (--dynamic-whitelist detected)')\n\n    try:\n        init(_CONF)\n        old_state = None\n        while True:\n            new_state = get_state()\n            # Log state transition\n            if new_state != old_state:\n                rpc.send_msg('*Status:* `{}`'.format(new_state.name.lower()))\n                logger.info('Changing state to: %s', new_state.name)\n\n            if new_state == State.STOPPED:\n                time.sleep(1)\n            elif new_state == State.RUNNING:\n                throttle(\n                    _process,\n                    min_secs=_CONF['internals'].get('process_throttle_secs', 10),\n                    dynamic_whitelist=args.dynamic_whitelist,\n                )\n            old_state = new_state\n    except KeyboardInterrupt:\n        logger.info('Got SIGINT, aborting ...')\n    except BaseException:\n        logger.exception('Got fatal exception!')\n    finally:\n        cleanup()\n\n\nif __name__ == '__main__':\n    main()\n", "path": "freqtrade/main.py"}], "after_files": [{"content": "#!/usr/bin/env python3\nimport copy\nimport json\nimport logging\nimport sys\nimport time\nimport traceback\nfrom datetime import datetime\nfrom typing import Dict, Optional, List\n\nimport requests\nfrom cachetools import cached, TTLCache\n\nfrom freqtrade import __version__, exchange, persistence, rpc, DependencyException, \\\n    OperationalException\nfrom freqtrade.analyze import get_signal, SignalType\nfrom freqtrade.misc import State, get_state, update_state, parse_args, throttle, \\\n    load_config\nfrom freqtrade.persistence import Trade\n\nlogger = logging.getLogger('freqtrade')\n\n_CONF = {}\n\n\ndef refresh_whitelist(whitelist: Optional[List[str]] = None) -> None:\n    \"\"\"\n    Check wallet health and remove pair from whitelist if necessary\n    :param whitelist: a new whitelist (optional)\n    :return: None\n    \"\"\"\n    whitelist = whitelist or _CONF['exchange']['pair_whitelist']\n\n    sanitized_whitelist = []\n    health = exchange.get_wallet_health()\n    for status in health:\n        pair = '{}_{}'.format(_CONF['stake_currency'], status['Currency'])\n        if pair not in whitelist:\n            continue\n        if status['IsActive']:\n            sanitized_whitelist.append(pair)\n        else:\n            logger.info(\n                'Ignoring %s from whitelist (reason: %s).',\n                pair, status.get('Notice') or 'wallet is not active'\n            )\n    if _CONF['exchange']['pair_whitelist'] != sanitized_whitelist:\n        logger.debug('Using refreshed pair whitelist: %s ...', sanitized_whitelist)\n        _CONF['exchange']['pair_whitelist'] = sanitized_whitelist\n\n\ndef _process(dynamic_whitelist: Optional[bool] = False) -> bool:\n    \"\"\"\n    Queries the persistence layer for open trades and handles them,\n    otherwise a new trade is created.\n    :param: dynamic_whitelist: True is a dynamic whitelist should be generated (optional)\n    :return: True if a trade has been created or closed, False otherwise\n    \"\"\"\n    state_changed = False\n    try:\n        # Refresh whitelist based on wallet maintenance\n        refresh_whitelist(\n            gen_pair_whitelist(_CONF['stake_currency']) if dynamic_whitelist else None\n        )\n        # Query trades from persistence layer\n        trades = Trade.query.filter(Trade.is_open.is_(True)).all()\n        if len(trades) < _CONF['max_open_trades']:\n            try:\n                # Create entity and execute trade\n                state_changed = create_trade(float(_CONF['stake_amount']))\n                if not state_changed:\n                    logger.info(\n                        'Checked all whitelisted currencies. '\n                        'Found no suitable entry positions for buying. Will keep looking ...'\n                    )\n            except DependencyException as e:\n                logger.warning('Unable to create trade: %s', e)\n\n        for trade in trades:\n            # Get order details for actual price per unit\n            if trade.open_order_id:\n                # Update trade with order values\n                logger.info('Got open order for %s', trade)\n                trade.update(exchange.get_order(trade.open_order_id))\n\n            if trade.is_open and trade.open_order_id is None:\n                # Check if we can sell our current pair\n                state_changed = handle_trade(trade) or state_changed\n\n            Trade.session.flush()\n    except (requests.exceptions.RequestException, json.JSONDecodeError) as error:\n        logger.warning(\n            'Got %s in _process(), retrying in 30 seconds...',\n            error\n        )\n        time.sleep(30)\n    except OperationalException:\n        rpc.send_msg('*Status:* Got OperationalException:\\n```\\n{traceback}```{hint}'.format(\n            traceback=traceback.format_exc(),\n            hint='Issue `/start` if you think it is safe to restart.'\n        ))\n        logger.exception('Got OperationalException. Stopping trader ...')\n        update_state(State.STOPPED)\n    return state_changed\n\n\ndef execute_sell(trade: Trade, limit: float) -> None:\n    \"\"\"\n    Executes a limit sell for the given trade and limit\n    :param trade: Trade instance\n    :param limit: limit rate for the sell order\n    :return: None\n    \"\"\"\n    # Execute sell and update trade record\n    order_id = exchange.sell(str(trade.pair), limit, trade.amount)\n    trade.open_order_id = order_id\n\n    fmt_exp_profit = round(trade.calc_profit(limit) * 100, 2)\n    rpc.send_msg('*{}:* Selling [{}]({}) with limit `{:.8f} (profit: ~{:.2f}%)`'.format(\n        trade.exchange,\n        trade.pair.replace('_', '/'),\n        exchange.get_pair_detail_url(trade.pair),\n        limit,\n        fmt_exp_profit\n    ))\n    Trade.session.flush()\n\n\ndef min_roi_reached(trade: Trade, current_rate: float, current_time: datetime) -> bool:\n    \"\"\"\n    Based an earlier trade and current price and ROI configuration, decides whether bot should sell\n    :return True if bot should sell at current rate\n    \"\"\"\n    current_profit = trade.calc_profit(current_rate)\n    if 'stoploss' in _CONF and current_profit < float(_CONF['stoploss']):\n        logger.debug('Stop loss hit.')\n        return True\n\n    # Check if time matches and current rate is above threshold\n    time_diff = (current_time - trade.open_date).total_seconds() / 60\n    for duration, threshold in sorted(_CONF['minimal_roi'].items()):\n        if time_diff > float(duration) and current_profit > threshold:\n            return True\n\n    logger.debug('Threshold not reached. (cur_profit: %1.2f%%)', current_profit * 100.0)\n    return False\n\n\ndef handle_trade(trade: Trade) -> bool:\n    \"\"\"\n    Sells the current pair if the threshold is reached and updates the trade record.\n    :return: True if trade has been sold, False otherwise\n    \"\"\"\n    if not trade.is_open:\n        raise ValueError('attempt to handle closed trade: {}'.format(trade))\n\n    logger.debug('Handling %s ...', trade)\n    current_rate = exchange.get_ticker(trade.pair)['bid']\n    if min_roi_reached(trade, current_rate, datetime.utcnow()) or get_signal(trade.pair, SignalType.SELL):\n        execute_sell(trade, current_rate)\n        return True\n    return False\n\n\ndef get_target_bid(ticker: Dict[str, float]) -> float:\n    \"\"\" Calculates bid target between current ask price and last price \"\"\"\n    if ticker['ask'] < ticker['last']:\n        return ticker['ask']\n    balance = _CONF['bid_strategy']['ask_last_balance']\n    return ticker['ask'] + balance * (ticker['last'] - ticker['ask'])\n\n\ndef create_trade(stake_amount: float) -> bool:\n    \"\"\"\n    Checks the implemented trading indicator(s) for a randomly picked pair,\n    if one pair triggers the buy_signal a new trade record gets created\n    :param stake_amount: amount of btc to spend\n    :return: True if a trade object has been created and persisted, False otherwise\n    \"\"\"\n    logger.info(\n        'Checking buy signals to create a new trade with stake_amount: %f ...',\n        stake_amount\n    )\n    whitelist = copy.deepcopy(_CONF['exchange']['pair_whitelist'])\n    # Check if stake_amount is fulfilled\n    if exchange.get_balance(_CONF['stake_currency']) < stake_amount:\n        raise DependencyException(\n            'stake amount is not fulfilled (currency={})'.format(_CONF['stake_currency'])\n        )\n\n    # Remove currently opened and latest pairs from whitelist\n    for trade in Trade.query.filter(Trade.is_open.is_(True)).all():\n        if trade.pair in whitelist:\n            whitelist.remove(trade.pair)\n            logger.debug('Ignoring %s in pair whitelist', trade.pair)\n    if not whitelist:\n        raise DependencyException('No pair in whitelist')\n\n    # Pick pair based on StochRSI buy signals\n    for _pair in whitelist:\n        if get_signal(_pair, SignalType.BUY):\n            pair = _pair\n            break\n    else:\n        return False\n\n    # Calculate amount\n    buy_limit = get_target_bid(exchange.get_ticker(pair))\n    amount = stake_amount / buy_limit\n\n    order_id = exchange.buy(pair, buy_limit, amount)\n    # Create trade entity and return\n    rpc.send_msg('*{}:* Buying [{}]({}) with limit `{:.8f}`'.format(\n        exchange.get_name().upper(),\n        pair.replace('_', '/'),\n        exchange.get_pair_detail_url(pair),\n        buy_limit\n    ))\n    # Fee is applied twice because we make a LIMIT_BUY and LIMIT_SELL\n    trade = Trade(\n        pair=pair,\n        stake_amount=stake_amount,\n        amount=amount,\n        fee=exchange.get_fee() * 2,\n        open_rate=buy_limit,\n        open_date=datetime.utcnow(),\n        exchange=exchange.get_name().upper(),\n        open_order_id=order_id\n    )\n    Trade.session.add(trade)\n    Trade.session.flush()\n    return True\n\n\ndef init(config: dict, db_url: Optional[str] = None) -> None:\n    \"\"\"\n    Initializes all modules and updates the config\n    :param config: config as dict\n    :param db_url: database connector string for sqlalchemy (Optional)\n    :return: None\n    \"\"\"\n    # Initialize all modules\n    rpc.init(config)\n    persistence.init(config, db_url)\n    exchange.init(config)\n\n    # Set initial application state\n    initial_state = config.get('initial_state')\n    if initial_state:\n        update_state(State[initial_state.upper()])\n    else:\n        update_state(State.STOPPED)\n\n\n@cached(TTLCache(maxsize=1, ttl=1800))\ndef gen_pair_whitelist(base_currency: str, topn: int = 20, key: str = 'BaseVolume') -> List[str]:\n    \"\"\"\n    Updates the whitelist with with a dynamically generated list\n    :param base_currency: base currency as str\n    :param topn: maximum number of returned results\n    :param key: sort key (defaults to 'BaseVolume')\n    :return: List of pairs\n    \"\"\"\n    summaries = sorted(\n        (s for s in exchange.get_market_summaries() if s['MarketName'].startswith(base_currency)),\n        key=lambda s: s.get(key) or 0.0,\n        reverse=True\n    )\n    return [s['MarketName'].replace('-', '_') for s in summaries[:topn]]\n\n\ndef cleanup() -> None:\n    \"\"\"\n    Cleanup the application state und finish all pending tasks\n    :return: None\n    \"\"\"\n    rpc.send_msg('*Status:* `Stopping trader...`')\n    logger.info('Stopping trader and cleaning up modules...')\n    update_state(State.STOPPED)\n    persistence.cleanup()\n    rpc.cleanup()\n    exit(0)\n\n\ndef main() -> None:\n    \"\"\"\n    Loads and validates the config and handles the main loop\n    :return: None\n    \"\"\"\n    global _CONF\n    args = parse_args(sys.argv[1:])\n    if not args:\n        exit(0)\n\n    # Initialize logger\n    logging.basicConfig(\n        level=args.loglevel,\n        format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',\n    )\n\n    logger.info(\n        'Starting freqtrade %s (loglevel=%s)',\n        __version__,\n        logging.getLevelName(args.loglevel)\n    )\n\n    # Load and validate configuration\n    _CONF = load_config(args.config)\n\n    # Initialize all modules and start main loop\n    if args.dynamic_whitelist:\n        logger.info('Using dynamically generated whitelist. (--dynamic-whitelist detected)')\n\n    try:\n        init(_CONF)\n        old_state = None\n        while True:\n            new_state = get_state()\n            # Log state transition\n            if new_state != old_state:\n                rpc.send_msg('*Status:* `{}`'.format(new_state.name.lower()))\n                logger.info('Changing state to: %s', new_state.name)\n\n            if new_state == State.STOPPED:\n                time.sleep(1)\n            elif new_state == State.RUNNING:\n                throttle(\n                    _process,\n                    min_secs=_CONF['internals'].get('process_throttle_secs', 10),\n                    dynamic_whitelist=args.dynamic_whitelist,\n                )\n            old_state = new_state\n    except KeyboardInterrupt:\n        logger.info('Got SIGINT, aborting ...')\n    except BaseException:\n        logger.exception('Got fatal exception!')\n    finally:\n        cleanup()\n\n\nif __name__ == '__main__':\n    main()\n", "path": "freqtrade/main.py"}]}