Esempio n. 1
0
class TestDataMethods(unittest.TestCase):
    def setUp(self):
        self.data = Data('testdb.sqlite')
        self.data.set('runTests', 'true')

    def test_initial_state(self):
        ''' self.data.keys will be empty at first '''
        self.assertEqual(self.data.keys[0], 'runTests')

    def test_get(self):
        ''' self.data.get test '''
        self.assertEqual(self.data.get('runTests'), 'true')

    def test_getAll(self):
        ''' set a couple more keys '''
        self.data.set('anotherKey', 'false')
        self.data.set('maybeOneMore', 'test')

        self.assertEqual(self.data.getAll(), {
            'anotherKey': 'false',
            'runTests': 'true',
            'maybeOneMore': 'test'
        })

    def test_delete(self):
        self.data.delete('runTests')

        self.assertEqual(self.data.get('runTests'), None)

    # def test_get(self):
    #     ''' set a key/value pair '''
    #     self.data.set('runTests','true')

    def tearDown(self):
        os.remove('./data/testdb.sqlite')
Esempio n. 2
0
 def _count_macd(self, date):
     """
     Moving Average Convergence/Divergence
     :link https://en.wikipedia.org/wiki/MACD
     """
     period1, period2, period3 = self.params
     if period1 > period2:
         # period1 should always be less than period2
         period1, period2 = period2, period2
     # Obtaining the needed data
     Count.count_once(
         ['EMA(' + str(period1) + ')', 'EMA(' + str(period2) + ')'],
         self.ticker,
         date=date)
     data = Data.get(
         ['EMA(' + str(period1) + ')', 'EMA(' + str(period2) + ')'],
         self.ticker,
         date=date)
     alpha = 2 / (period3 + 1)
     initial_sum = 0
     prev_signal = 0
     for index, (dtime, ema1, ema2) in enumerate(data):
         ema_diff = ema1 - ema2
         if index < period3 - 1:
             initial_sum += ema_diff
             continue
         elif index == period3 - 1:
             signal = round((initial_sum + ema_diff) / period3)
         else:
             signal = round(ema_diff * alpha + prev_signal * (1 - alpha))
         self.update(dtime, signal)
         prev_signal = signal
     self.commit()
Esempio n. 3
0
File: SMA.py Progetto: rsukhar/ft
 def _count_sma(self, date):
     """
     Simple Moving Average
     :link https://en.wikipedia.org/wiki/Moving_average#Simple_moving_average
     """
     data = Data.get(['vwap'], self.ticker, date=date, market_hours=True)
     period = self.params[0]
     prices = []
     for dtime, price in data:
         prices.append(price)
         if len(prices) > period:
             del prices[0]
         if len(prices) == period:
             value = round(sum(prices) / period)
             self.update(dtime, value)
     self.commit()
Esempio n. 4
0
File: EMA.py Progetto: rsukhar/ft
 def _count_ema(self, date):
     """
     Exponential Moving Average
     :link https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average
     """
     data = Data.get(['vwap'], self.ticker, date=date, market_hours=True)
     period = self.params[0]
     alpha = 2 / (period + 1)
     initial_sum = 0
     prev_value = 0
     for index, (dtime, price) in enumerate(data):
         if index < period - 1:
             initial_sum += price
             continue
         elif index == period - 1:
             value = round((initial_sum + price) / period)
         else:
             value = round(price * alpha + prev_value * (1 - alpha))
         self.update(dtime, value)
         prev_value = value
     self.commit()
Esempio n. 5
0
 def count_once(indicators, ticker, date=None):
     """ Count the requested set of indicators if they were not counted previously """
     indicators = Count.__fix_indicators_list(indicators)
     # Getting data 1-minute slice from the very middle of the day to check which data presents and which is missing
     if date is not None and isinstance(date, str):
         date = datetime.datetime.strptime(date, '%Y-%m-%d')
     date_mid = tradetime.daymid(date)
     missing_indicators = []
     data = list(
         Data.get(indicators,
                  ticker,
                  dtime_from=date_mid,
                  dtime_to=date_mid,
                  market_hours=False))
     if len(data) == 0:
         missing_indicators = list(indicators)
     else:
         for index, value in enumerate(data[0]):
             if index > 0 and value is None:
                 missing_indicators.append(indicators[index - 1])
     if len(missing_indicators) > 0:
         Count.count(missing_indicators, ticker, date)
Esempio n. 6
0
File: Chance.py Progetto: rsukhar/ft
 def _count_chance(self, date):
     """
     Relative price difference in the next N minutes
     """
     data = Data.get(['open', 'vwap'],
                     self.ticker,
                     date=date,
                     market_hours=True,
                     order='desc')
     period = self.params[0]
     history = []
     index = 0
     for dtime, open, vwap in data:
         index += 1
         if index > period:
             index = 1
         if len(history) < index:
             history.append(vwap)
         else:
             value = round((history[index - 1] - open) / open * 100, 2)
             history[index - 1] = vwap
             self.update(dtime, value)
     self.commit()
Esempio n. 7
0
class Exchange:
    """This class is used to interact with the market. It's a wrapper for a ccxt exchange object.
	"""
    def __init__(self,
                 logger,
                 data,
                 capital_allowed,
                 live_mode,
                 default_ticker=None,
                 default_period=None):
        """
		- logger: a Logger instance linked to a bot
		- data: a Data instance linked to a bot
		- capital_allowed: float, percentage (0-100) of the capital this instance is able to trade with
		- live_mode: bool, should we make real orders on the markets or simulate them
		- default_ticker: string, the ticker formatted like that: ASSET1/ASSET2, optional 
		- default_period: string, the period resolution you want to get data in (1m, 5m, 1h...), optional
		"""
        self.logger = logger
        self.data = data
        self.capital_allowed = capital_allowed
        self.default_ticker = default_ticker
        self.default_period = default_period
        self.live_mode = live_mode
        self.fake_balance = []
        self.fake_pnl = []
        self.fake_current_price = None
        self.fake_current_date = None
        self.fake_orders = []
        if (self.default_period and self.default_ticker):
            self.cache = Data(
                f"{self.default_period}-{''.join(self.default_ticker.split('/'))}-cache"
            )
        else:
            self.cache = None
        self.client = ccxt.binance(config.get_creds()['binance'])

    def candles_to_df(self, candles):
        data = pd.DataFrame()
        data['timestamp'] = candles[:, 0]
        data['date'] = pd.to_datetime(data['timestamp'] * 1000000)
        data['open'] = candles[:, 1]
        data['high'] = candles[:, 2]
        data['low'] = candles[:, 3]
        data['close'] = candles[:, 4]
        data['volume'] = candles[:, 5]
        return data

    def get_latest_data(self, ticker=None, period=None, length=0):
        """Fetch the latest data for the given ticker. It will return at least lenth candles.
		
		- ticker: string, the ticker formatted like that: ASSET1/ASSET2, optional if default is set
		- period: string, the period resolution you want (1m, 5m, 1h...), optional is default is set
		- length: int, the minimum number of past candles you want, optional
		Returns a pandas dataframe.
		"""
        if (ticker is None):
            ticker = self.default_ticker
        if (period is None):
            period = self.default_period
        candles = None
        try:
            candles = np.array(self.client.fetch_ohlcv(ticker, period))
        except:
            self.logger.log(
                "❗️", "Unable to fetch live data, retrying in 10 seconds")
            time.sleep(10)
            return self.get_latest_data(ticker, length)
        return self.candles_to_df(candles)

    def get_data(self, start_date, end_date, ticker=None, period=None):
        """Get historical data between the given dates.

		- start_date: string, a date formatted in ISO 8601 from when to download data
		- end_date: string, a date formatted in ISO 8601
		"""
        if (ticker is None):
            ticker = self.default_ticker
        if (period is None):
            period = self.default_period
        start = self.client.parse8601(start_date)
        end = self.client.parse8601(end_date)

        candles = None
        last_date = start
        while (last_date < end):
            print(
                f"Downloading {datetime.utcfromtimestamp(last_date / 1000).isoformat()}"
            )
            if (not self.cache is None
                    and not self.cache.get(last_date) is None):
                new_candles = self.cache.get(last_date)
                print("Found in cache")
            else:
                new_candles = np.array(
                    self.client.fetch_ohlcv(ticker, period, last_date))
                if (not self.cache is None):
                    self.cache.set(last_date, new_candles)
                time.sleep(1)
            if (candles is None):
                candles = new_candles
            else:
                candles = np.vstack([candles, new_candles])
            last_date = int(candles[-1][0])
        df = self.candles_to_df(candles)
        return df

    def buy(self, ticker=None, max_try=3):
        """Buy the given ticker.

		- ticker: string, the ticker formatted like that: ASSET1/ASSET2, optional if default is set
		Returns a trade object.
		"""
        if (max_try <= 0):
            self.logger.log("❌", "Failed 3 times to buy, giving up")
            return None
        if (ticker is None):
            ticker = self.default_ticker
        if (not self.live_mode):
            return self.fake_buy(ticker)
        asset1 = ticker.split("/")[0]
        asset2 = ticker.split("/")[1]
        balance = self.get_balance(asset2)
        price = self.client.fetch_ticker(ticker)['last']
        proportion = (self.capital_allowed / 100)
        qty = (balance * proportion) / price
        qty = self.client.amount_to_precision(ticker, qty)
        try:
            self.logger.log("ℹ️", f"Buying {qty}{asset1}")
            trade = self.client.create_market_buy_order(ticker, qty)
            self.logger.log(
                "💵", f"Bought {qty}{asset1} for {trade['price']:.2f}{asset2}")
            self.logger.order('buy', trade['price'], trade['cost'])
            self.data.set("buy_cost", trade['cost'])
            return trade
        except:
            traceback.print_exc()
            self.logger.log("❌", "Cannot buy, retrying in 3 seconds")
            time.sleep(3)
            return self.buy(ticker, max_try - 1)

    def sell(self, ticker=None, max_try=3):
        """Sell the given ticker.

		- ticker: string, the ticker formatted like that: ASSET1/ASSET2, optional if default is set
		Returns a trade object.
		"""
        if (max_try <= 0):
            self.logger.log("❌", "Failed 3 times to sell, giving up")
            return None
        if (ticker is None):
            ticker = self.default_ticker
        if (not self.live_mode):
            return self.fake_sell(ticker)
        asset1 = ticker.split("/")[0]
        asset2 = ticker.split("/")[1]
        balance = self.get_balance(asset1)
        try:
            self.logger.log("ℹ️", f"Selling {balance}{asset1}")
            trade = self.client.create_market_sell_order(ticker, balance)
            self.logger.log(
                "💵", f"Sold {balance}{asset1} for {trade['price']}{asset2}")
            self.logger.order('sell', trade['price'], trade['cost'])
            balance_diff = trade['cost'] - self.data.get("buy_cost")
            self.logger.pnl(balance_diff / self.data.get("buy_cost") * 100,
                            balance_diff)
            self.logger.balance(self.get_balance())
            self.data.remove("buy_cost")
            return trade
        except:
            traceback.print_exc()
            self.logger.log("❌", "Cannot sell, retrying in 3 seconds")
            time.sleep(3)
            return self.sell(ticker, max_try - 1)

    def fake_buy(self, ticker):
        """This functions creates a fake buy order.
		The buy function of a bot in backtracking mode is redirected here.
		It's called automatically by the backtracking algorithm, you shouldn't
		have to use it.
		"""
        self.data.set("buy_price", self.fake_current_price)
        self.fake_orders.append({
            'action': 'buy',
            'date': self.fake_current_date
        })

    def fake_sell(self, ticker):
        """This functions creates a fake sell order.
		The sell function of a bot in backtracking mode is redirected here.
		It's called automatically by the backtracking algorithm, you shouldn't
		have to use it.
		It will calculate estimated PNL for the trade.
		"""
        diff_cost = self.fake_current_price - self.data.get("buy_price")
        diff_per = (diff_cost / self.data.get("buy_price")) * 100
        diff_per -= 0.2
        profit = self.fake_balance[-1] * (diff_per / 100)
        self.fake_balance.append(self.fake_balance[-1] + profit)
        self.fake_pnl.append(diff_per)
        self.data.remove("buy_price")
        self.fake_orders.append({
            'action': 'sell',
            'date': self.fake_current_date
        })

    def get_balance(self, asset=None):
        """Get the balance of the account for the given asset.

		- asset: string, the asset to check, optional if default is set
		Returns the current asset balance as float.
		"""
        if (not self.live_mode):
            return None
        if (asset is None):
            asset = self.default_ticker.split("/")[1]
        try:
            return self.client.fetch_balance()[asset]['free']
        except:
            self.logger.log("❌", f"Cannot fetch balance for {asset}")
            return None

    def init_fake_balance(self):
        """This functions initializes the backtesting fake balance array
		"""
        self.fake_balance = [100]
        self.fake_pnl = []
        self.fake_orders = []