Пример #1
0
 def _setup(self, config):
     # Initialize a Buda.com and reference exchange clients
     self.market = Market.from_code(config['investment']['market'])
     self.ref_market = Market.from_code(config['investment']['ref_market'])
     self.buda = buda.BudaTrading(market=self.market, dry_run=self.dry_run)
     self.reference = self.get_market_client(
         config['investment']['ref_exchange'], self.ref_market)
     # Initialize Algorithm configs
     self.daily_investment = Money(
         str(config['investment']['monthly_amount'] / 30),
         self.market.quote)
     self.interval_hours = config['investment']['interval_hours']
     self.transactions = self.store.get(
         f'transactions_{self.market}'.lower()) or []
     self.amount_investment = self.calculate_amount_investment()
     assert self.amount_investment > Money(
         '1', self.market.quote), 'Amount investment too low'
     self.overprice_limit = config['investment']['overprice_limit']
     # Currency converter to use
     self.converter = config['currency_converter']
     self.rate = self.get_converter_rate()
     # Set withdrawal configs
     self.withdrawal_enabled = config['withdrawal']['enabled']
     self.withdrawal_address = config['withdrawal']['address']
     w_amount = str(config['withdrawal']['min_amount'])
     self.minimum_withdrawal_amount = Money(
         w_amount, config['withdrawal']['amount_currency'])
Пример #2
0
 def update_deposits(self):
     # Set wallet from relevant currency according to side
     from_wallet = (
         self.buda.wallets.quote if self.side == Side.BUY else self.buda.wallets.base
     )
     # Get and filter deposits
     new_deposits = from_wallet.fetch_deposits_since(self.start_timestamp)
     if self.from_address != "Any":
         new_deposits = [
             deposit
             for deposit in new_deposits
             if deposit.data.address == self.from_address
         ]
     # Update states on existing keys and add new keys with base structure
     for deposit in new_deposits:
         idx = str(deposit.id)
         if idx in self.deposits.keys():
             if deposit.status.value != self.deposits[idx]["status"]:
                 self.deposits[idx]["status"] = deposit.status
         else:
             self.deposits[idx] = {
                 "status": deposit.status.value,
                 "amounts": {
                     "original_amount": repr(deposit.amount),
                     "converted_amount": repr(Money(0, self.from_currency)),
                     "converted_value": repr(Money(0, self.to_currency)),
                 },
                 "orders": [],
                 "pending_withdrawal": self.to_withdraw,
             }
         self.store.set(self.from_currency + "_deposits", self.deposits)
Пример #3
0
 def _setup(self, config):
     # Set buda trading client
     client_params = dict(timeout=self.timeout)
     self.buda = BudaTrading(
         config["market"], client_params, self.dry_run, self.log, self.store
     )
     # Set price multipliers
     self.buy_multiplier = Decimal(str(config["prices"]["buy_multiplier"]))
     self.sell_multiplier = Decimal(str(config["prices"]["sell_multiplier"]))
     self.max_base = Money(config["amounts"]["max_base"], self.buda.market.base)
     self.max_quote = Money(config["amounts"]["max_quote"], self.buda.market.quote)
Пример #4
0
    def _algorithm(self):
        if not self.invest:
            self.log.info('Not investing')
            return
        balance = self.buda.wallets.quote.fetch_balance().free
        international_price = self.reference.fetch_ticker().last
        buy_amount = self.get_amount_to_buy()

        buy_price = truncate_money(
            Money(self.amount_investment.amount / buy_amount.amount,
                  self.market.quote))
        reference_price = truncate_money(
            Money(international_price.amount * self.rate.amount,
                  self.rate.currency))

        self.log.info(f'I have {balance}')
        self.log.info(f'I will invest {self.amount_investment}')
        self.log.info(
            f'{self.ref_market.quote}{self.market.quote}: {self.rate}')
        self.log.info(f'{self.market.base} to buy: {buy_amount}')

        # Log the price
        self.log.info(
            f'International {self.ref_market} price: {international_price}')
        self.log.info(f'International {self.market} price: {reference_price}')

        self.log.info(f'---------------------------------------')
        self.log.info(f'Buy price: {buy_price}')
        self.log.info(
            f'Overprice: {(self.get_overprice(buy_price, reference_price)):.2%}'
        )
        self.log.info(
            f'Should buy? {(self.should_buy(buy_price, reference_price, balance))}'
        )
        self.log.info(f'---------------------------------------')

        if not self.already_transacted():
            if self.should_buy(buy_price, reference_price, balance):
                if self.send_buy_order(buy_amount):
                    self.store_transaction(buy_price, self.amount_investment,
                                           buy_amount)
                else:
                    self.log.info(f'Problem with order')
            else:
                self.log.info(f'Not investing')
        else:
            self.log.info(f'Already transacted')

        if self.withdrawal_enabled:
            self.log.info(
                f'Withdrawal enabled to address {self.withdrawal_address}')
            self.withdraw_to_own_wallet(reference_price)
        else:
            self.log.info(f'Withdrawal not enabled')
Пример #5
0
 def _get_reference_prices(self):
     ticker = self.reference.fetch_ticker()
     ref_bid, ref_ask = ticker.bid, ticker.ask
     # Convert reference_price if reference market differs from current market
     if self.reference.market != self.buda.market:
         # Get conversion rate (eg CLP/USD from OpenExchangeRates)
         rate = self.converter.get_rate_for(self.reference.market.quote,
                                            self.buda.market.quote)
         self.log.info(
             f'{self.reference.market.quote}/{self.buda.market.quote} rate: {rate:.2f}'
             f' from {self.converter.name}')
         # Get market price according to reference (eg BTC/CLP converted from converter's BTC/USD)
         ref_bid = Money(ref_bid.amount * rate, self.buda.market.quote)
         ref_ask = Money(ref_ask.amount * rate, self.buda.market.quote)
     return ref_bid, ref_ask
Пример #6
0
 def get_converter_rate(self):
     # Set currency converter
     if self.converter == 'OpenExchangeRates':
         app_id = settings.credentials['OpenExchangeRates']['app_id']
         converter = OpenExchangeRates(return_decimal=True,
                                       client_params=dict(app_id=app_id))
         return truncate_money(
             Money(
                 converter.convert(1, self.ref_market.quote,
                                   self.market.quote), self.market.quote))
     elif self.converter == 'Currencyconverterapi':
         api_key = settings.credentials['Currencyconverter']['key']
         code = f'{self.ref_market.quote}_{self.market.quote}'
         rate = requests.get(
             f'https://free.currencyconverterapi.com/api/v6/convert?q={code}&compact=y&apiKey={api_key}'
         ).json()[code]['val']
         return truncate_money(Money(rate, self.market.quote))
Пример #7
0
 def process_withdrawals(self):
     # Set wallet from relevant currency according to side
     to_wallet = (
         self.buda.wallets.base if self.side == Side.BUY else self.buda.wallets.quote
     )
     for deposit in self.deposits.values():
         # Load money amounts
         original_amount = Money.loads(deposit["amounts"]["original_amount"])
         converted_amount = Money.loads(deposit["amounts"]["converted_amount"])
         converted_value = Money.loads(deposit["amounts"]["converted_value"])
         remaining = original_amount - converted_amount
         if (
             self.side == Side.BUY
         ):  # Change remaining amount to base currency for minimum order amount check
             remaining = (
                 self.buda.fetch_order_book().quote(self.side, remaining).base_amount
             )
         # Filter deposits already converted and pending withdrawal
         original_amount_is_converted = remaining < self.buda.min_order_amount
         if (
             deposit["status"] == TxStatus.OK.value
             and deposit["pending_withdrawal"]
             and original_amount_is_converted
         ):
             withdrawal_amount = truncate_money(converted_value)
             available = to_wallet.fetch_balance().free
             if (
                 withdrawal_amount <= available
             ):  # We cannot withdraw more than available balance
                 w = to_wallet.request_withdrawal(
                     withdrawal_amount, self.to_address, subtract_fee=True
                 )
                 if (
                     w.status == TxStatus.PENDING
                 ):  # Check state to set and store updated values
                     self.log.info(
                         f"{self.to_currency} withdrawal request received, updating store values"
                     )
                     deposit["pending_withdrawal"] = False
                     self.store.set(self.from_currency + "_deposits", self.deposits)
                 else:
                     self.log.warning("Withdrawal failed")
             else:
                 self.log.warning(
                     f"Available balance not enough for withdrawal amount {withdrawal_amount}"
                 )
Пример #8
0
 def process_conversions(self):
     for deposit in self.deposits.values():
         # Calculate remaining amount to convert
         original_amount = Money.loads(deposit["amounts"]["original_amount"])
         converted_amount = Money.loads(deposit["amounts"]["converted_amount"])
         converted_value = Money.loads(deposit["amounts"]["converted_value"])
         remaining = original_amount - converted_amount
         if (
             self.side == Side.BUY
         ):  # Change remaining amount to base currency for order creation purposes
             remaining = (
                 self.buda.fetch_order_book().quote(self.side, remaining).base_amount
             )
         if (
             deposit["status"] == TxStatus.OK.value
             and remaining > self.buda.min_order_amount
         ):
             remaining = truncate_money(remaining)
             # Convert remaining amount using market order
             order = self.buda.place_market_order(self.side, remaining)
             # Wait for traded state to set updated values
             if order:
                 self.log.info(
                     f"{self.side} market order placed, waiting for traded state"
                 )
                 while order.status != OrderStatus.CLOSED:
                     order = self.buda.fetch_order(order.id)
                     maya.time.sleep(1)
                 self.log.info(f"{self.side} order traded, updating store values")
                 if self.side == Side.BUY:
                     converted_amount += order.cost
                     converted_value += order.filled - order.fee
                 if self.side == Side.SELL:
                     converted_amount += order.filled
                     converted_value += order.cost - order.fee
                 deposit["orders"].append(
                     order.id
                 )  # Save related orders for debugging
             # Save new values __str__
             deposit["amounts"]["converted_amount"] = repr(converted_amount)
             deposit["amounts"]["converted_value"] = repr(converted_value)
     # Store all deposits
     self.store.set(self.from_currency + "_deposits", self.deposits)
Пример #9
0
 def _setup(self, config):
     # Set buda trading client
     client_params = dict(timeout=self.timeout)
     self.buda = buda.BudaTrading(
         config["market"], client_params, self.dry_run, self.log, self.store
     )
     self.store_keys = ("trades", self.buda.market.code.lower())
     # Set reference market client
     self.candle_interval = config["reference"]["candle_interval"]
     self.reference = self._get_market_client(
         config["reference"]["name"], config["reference"]["market"]
     )
     assert self.reference.market.base == self.buda.market.base
     # Init variables
     self.max_base = Money(config["amounts"]["max_base"], self.buda.market.base)
     self.max_quote = Money(config["amounts"]["max_quote"], self.buda.market.quote)
     self.position = self.store.get("position") or dict(status="closed")
     # Set talib configs
     self.bbands_periods = config["talib"]["bbands"]["periods"]
     self.rsi_periods = config["talib"]["rsi"]["periods"]
     self.rsi_overbought = config["talib"]["rsi"]["overbought"]
     self.rsi_oversold = config["talib"]["rsi"]["oversold"]
Пример #10
0
 def _setup(self, config):
     # Set buda trading client
     client_params = dict(timeout=self.timeout)
     self.buda = buda.BudaTrading(
         config["market"], client_params, self.dry_run, self.log, self.store
     )
     # Set reference market client
     self.reference = self._get_market_client(
         config["reference"]["name"], config["reference"]["market"]
     )
     assert self.reference.market.base == self.buda.market.base
     # Set converter
     app_id = settings.credentials["OpenExchangeRates"]["app_id"]
     self.converter = OpenExchangeRates(
         return_decimal=True, client_params=dict(app_id=app_id)
     )
     # Set price multipliers
     self.buy_multiplier = Decimal(str(config["prices"]["buy_multiplier"]))
     self.sell_multiplier = Decimal(str(config["prices"]["sell_multiplier"]))
     # Set max amounts
     self.max_base = Money(config["amounts"]["max_base"], self.buda.market.base)
     self.max_quote = Money(config["amounts"]["max_quote"], self.buda.market.quote)
Пример #11
0
 def _setup(self, config):
     # Set buda trading client
     client_params = dict(timeout=self.timeout)
     self.buda = buda.BudaTrading(config['market'], client_params,
                                  self.dry_run, self.log, self.store)
     self.store_keys = ('trades', self.buda.market.code.lower())
     # Set reference market client
     self.candle_interval = config['reference']['candle_interval']
     self.reference = self._get_market_client(config['reference']['name'],
                                              config['reference']['market'])
     assert self.reference.market.base == self.buda.market.base
     # Init variables
     self.max_base = Money(config['amounts']['max_base'],
                           self.buda.market.base)
     self.max_quote = Money(config['amounts']['max_quote'],
                            self.buda.market.quote)
     self.position = self.store.get('position') or dict(status='closed')
     # Set talib configs
     self.bbands_periods = config['talib']['bbands']['periods']
     self.rsi_periods = config['talib']['rsi']['periods']
     self.rsi_overbought = config['talib']['rsi']['overbought']
     self.rsi_oversold = config['talib']['rsi']['oversold']
Пример #12
0
 def _algorithm(self):
     # Setup
     self.log.info(
         f"Preparing prices using {self.reference.name} {self.reference.market.code}"
     )
     ref_bid, ref_ask = self._get_reference_prices()
     self.log.info(
         f"Reference prices on {self.reference.name}: Bid: {ref_bid} Ask: {ref_ask}"
     )
     # Set offset prices from reference and price multiplier
     price_buy = truncate_money(ref_bid * self.buy_multiplier)
     price_sell = truncate_money(ref_ask * self.sell_multiplier)
     self.log.info(
         f"{self.buda.market} calculated prices: Buy: {price_buy} Sell: {price_sell}"
     )
     # Cancel open orders
     self.log.info("Closing open orders")
     self.buda.cancel_all_orders()
     # Get available balances
     self.log.info(f"Preparing amounts")
     # Fetch available balances
     available_base = self.buda.wallets.base.fetch_balance().free
     available_quote = self.buda.wallets.quote.fetch_balance().free
     # Adjust amounts to max in config
     amount_base = min(self.max_base, available_base)
     amount_quote = min(self.max_quote, available_quote)
     self.log.info(f"Amounts | Bid: {amount_base} | Ask: {amount_quote}")
     # Get order buy and sell amounts
     # *quote amount must be converted to base
     amount_buy = truncate_money(
         Money(amount_quote / price_buy, self.buda.market.base)
     )
     amount_sell = truncate_money(amount_base)
     self.log.info(f"Amounts | Buy {amount_buy} | Sell {amount_sell}")
     # PLACE ORDERS
     self.log.info("Starting order deployment")
     if amount_buy >= self.buda.min_order_amount:
         self.buda.place_limit_order(
             side=Side.BUY, amount=amount_buy, price=price_buy
         )
     if amount_sell >= self.buda.min_order_amount:
         self.buda.place_limit_order(
             side=Side.SELL, amount=amount_sell, price=price_sell
         )
Пример #13
0
 def withdraw_to_own_wallet(self, reference_price):
     if self.minimum_withdrawal_amount.currency == self.market.quote:
         min_amount = truncate_money(
             Money(
                 self.minimum_withdrawal_amount.amount /
                 reference_price.amount, self.market.base))
     else:
         min_amount = truncate_money(self.minimum_withdrawal_amount)
     assert min_amount.currency == self.market.base, 'Something is wrong with withdrawal currency'
     balance = self.buda.wallets.base.fetch_balance().free
     if balance >= min_amount:
         self.log.info(
             f'Requesting withdrawals of {balance} to {self.withdrawal_address}'
         )
         self.buda.wallets.base.request_withdrawal(balance,
                                                   self.withdrawal_address,
                                                   substract_fee=True)
     else:
         self.log.info(f'Not enough balance: {balance} < {min_amount}')
Пример #14
0
 def _algorithm(self):
     # PREPARE ORDER PRICES
     # Get middle price
     ticker = self.buda.fetch_ticker()
     self.log.info(
         f"Ticker prices   | Bid: {ticker.bid} | Ask: {ticker.ask} | Mid: {ticker.mid}"
     )
     # Offset prices from middle using configured price multipliers
     price_buy = truncate_money(ticker.mid * self.buy_multiplier)
     price_sell = truncate_money(ticker.mid * self.sell_multiplier)
     self.log.info(f"Relative prices | Buy: {price_buy} | Sell: {price_sell}")
     # PREPARE ORDER AMOUNTS
     # Cancel open orders to get correct available amounts
     self.log.info("Closing open orders")
     self.buda.cancel_all_orders()
     # Fetch available balances
     available_base = self.buda.wallets.base.fetch_balance().free
     available_quote = self.buda.wallets.quote.fetch_balance().free
     # Adjust amounts to max in config
     amount_base = min(self.max_base, available_base)
     amount_quote = min(self.max_quote, available_quote)
     # Get order buy and sell amounts
     # *quote amount must be converted to base
     amount_buy = truncate_money(
         Money(amount_quote / price_buy, self.buda.market.base)
     )
     amount_sell = truncate_money(amount_base)
     self.log.info(f"Amounts | Buy {amount_buy} | Sell {amount_sell}")
     # PLACE ORDERS
     self.log.info("Starting order deployment")
     if amount_buy >= self.buda.min_order_amount:
         self.buda.place_limit_order(
             side=Side.BUY, amount=amount_buy, price=price_buy
         )
     if amount_sell >= self.buda.min_order_amount:
         self.buda.place_limit_order(
             side=Side.SELL, amount=amount_sell, price=price_sell
         )
Пример #15
0
 def _algorithm(self):
     # Update candle data and TA indicators
     self.log.info(
         f"Getting trades from {self.reference.name} {self.reference.market.code}"
     )
     from_time = maya.when("1 day ago").epoch
     trades = self.get_trades(from_time)
     # Create pandas DataFrame from trades and set date as index
     df = pd.DataFrame(trades)
     df.index = df.timestamp.apply(lambda x: pd.datetime.utcfromtimestamp(x))
     # Build 5min candles from trades DataFrame [open, high, close, low]
     df = df.rate.resample(self.candle_interval).ohlc()
     # Calculate Bollinger Bands and RSI from talib
     df["bb_lower"], df["bb_middle"], df["bb_upper"] = talib.BBANDS(
         df.close, timeperiod=self.bbands_periods
     )
     df["rsi"] = talib.RSI(df.close, timeperiod=self.rsi_periods)
     lower = self.truncate_price(df.bb_lower[-1])
     middle = self.truncate_price(df.bb_middle[-1])
     upper = self.truncate_price(df.bb_upper[-1])
     self.log.info(f"BB_lower: {lower} | BB_middle: {middle} | BB_upper: {upper}")
     self.log.info(f"RSI: {df.rsi[-1]:.2f}")
     # Check if our position is open or closed
     if self.position["status"] == "closed":
         # Try conditions to open position
         self.log.info(f"Position is closed, checking to open")
         if df.close[-1] < df.bb_lower[-1] and df.rsi[-1] < self.rsi_oversold:
             # Last price is lower than the lower BBand and RSI is oversold, BUY!
             self.log.info(f"Market oversold! BUY!")
             amount = self.get_amount(Side.BUY)
             if amount >= self.buda.min_order_amount:
                 tx = self.buda.place_market_order(Side.BUY, amount)
                 self.position = {
                     "status": "open",
                     "side": Side.BUY.value,
                     "amount": repr(tx.amount),
                 }
             else:
                 self.log.warning(
                     f"Available amount is lower than minimum order amount"
                 )
         elif df.close[-1] > df.bb_upper[-1] and df.rsi[-1] > self.rsi_overbought:
             # Last price is higher than the upper BBand and RSI is overbought, SELL!
             self.log.info(f"Market overbought! SELL!")
             amount = self.get_amount(Side.SELL)
             if amount >= self.buda.min_order_amount:
                 tx = self.buda.place_market_order(Side.SELL, amount)
                 self.position = {
                     "status": "open",
                     "side": Side.SELL.value,
                     "amount": repr(tx.amount),
                 }
             else:
                 self.log.warning(
                     f"Available amount is lower than minimum order amount"
                 )
         else:
             self.log.info(f"Market conditions unmet to open position")
     else:
         self.log.info(f"Position is open, checking to close")
         if self.position["side"] == Side.BUY.value and df.rsi[-1] >= 30:
             # RSI is back to normal, close Buy position
             self.log.info(f"Market is back to normal, closing position")
             amount = Money.loads(self.position["amount"])
             if amount >= self.buda.min_order_amount:
                 tx = self.buda.place_market_order(Side.SELL, amount)
                 remaining = amount - tx.amount
                 if remaining < self.buda.min_order_amount:
                     self.position = {"status": "closed"}
                 else:
                     self.position["amount"] = remaining
             else:
                 self.log.warning(
                     f"Available amount is lower than minimum order amount"
                 )
         elif self.position["side"] == Side.SELL.value and df.rsi[-1] <= 70:
             # RSI is back to normal, close Sell position
             self.log.info(f"Market is back to normal, closing position")
             amount = Money.loads(self.position["amount"])
             if amount >= self.buda.min_order_amount:
                 tx = self.buda.place_market_order(Side.BUY, amount)
                 remaining = amount - tx.amount
                 if remaining < self.buda.min_order_amount:
                     self.position = {"status": "closed"}
                 else:
                     self.position["amount"] = repr(remaining)
             else:
                 self.log.warning(
                     f"Available amount is lower than minimum order amount"
                 )
         else:
             self.log.info(f"Market conditions unmet to close position")
     self.store.set("position", self.position)