def stubbed(*coins): tickers = [] for coin in coins: last_ticker = (Ticker .select() .where(Ticker.coin == coin) .order_by(Ticker.captured_at.desc()) .limit(1)) if last_ticker.count() == 0: price = random.uniform(500, 12000) print(f'Creating first ticker for {coin.symbol}: {price} 0%') ticker = Ticker.create( coin=coin, price=price, price_change_day_pct=0, ) tickers.append(ticker) else: last_ticker = last_ticker.get() new_price = last_ticker.price * Decimal(random.uniform(0.98, 1.02)) price_change_day_pct = (new_price - last_ticker.price) / last_ticker.price print(f'{coin.symbol}: {new_price} {price_change_day_pct}%') ticker = Ticker.create(coin=coin, price=new_price, price_change_day_pct=price_change_day_pct) check_price_alerts(ticker) check_global_timed_game() tickers.append(ticker) return tickers
def save_insider_trades(tmp_file: IO): """Прочитать данные о торгах инсайдеров из файла и сохранить их в базу. :param tmp_file: временный файл, откуда откуда будут прочитаны данные и сохранены в файл """ for chunk in chunked(tmp_file.readlines(), Config.CHUNK_SIZE): insider_trades = [json.loads(line) for line in chunk] insiders: Set[InsiderTuple] = set( InsiderTuple( id=insider_trade['insider_id'], name=insider_trade['insider_name'], ) for insider_trade in insider_trades) Insider.insert_insiders(insiders) symbols: Set[str] = set(insider_trade['symbol'] for insider_trade in insider_trades) Ticker.insert_tickers(symbols) symbol_to_uuid: Dict[str, UUID] = Ticker.get_uuids_by_symbol() InsiderTrade.bulk_insert([ dict(ticker_id=symbol_to_uuid[insider_trade['symbol']], **{ k: v for k, v in insider_trade.items() if k != 'symbol' and k != 'insider_name' }) for insider_trade in insider_trades ]) session.commit()
def calc_change_percent(sess, ticker, start_time, now, peak=True): if peak: start = Ticker.peak(sess, ticker, start_time=start_time, now=now) else: start = Ticker.current_ask(sess, ticker, start_time) current = Ticker.current_ask(sess, ticker, now) if current is None or start is None: log.debug(f"Could not get current/start for {ticker} at s={start_time} n={now}: c={current}, p={start}") return 0, current return round(100 * (current - start) / start, 2), current
def test_parsing_with_many_coins(self): ping('BTC', 'ETH') btc = Coin.get(Coin.symbol == 'BTC') eth = Coin.get(Coin.symbol == 'ETH') btc_ticker = Ticker.get(Ticker.coin == btc) eth_ticker = Ticker.get(Ticker.coin == eth) self.assertEqual(btc_ticker.price, Decimal('56.12345678')) self.assertEqual(btc_ticker.price_change_day_pct, Decimal('-1.23456789')) self.assertEqual(eth_ticker.price, Decimal('42.98765432')) self.assertEqual(eth_ticker.price_change_day_pct, Decimal('-8.98765432'))
def test_parsing_with_many_ping_calls(self): ping('BTC') btc = Coin.get(Coin.symbol == 'BTC') btc_ticker = Ticker.get(Ticker.coin == btc) self.assertEqual(btc_ticker.price, Decimal('56.12345678')) self.assertEqual(btc_ticker.price_change_day_pct, Decimal('-2.33334567')) Ticker.delete().execute() ping('BTC') btc = Coin.get(Coin.symbol == 'BTC') btc_ticker = Ticker.get(Ticker.coin == btc) self.assertEqual(btc_ticker.price, Decimal('56.12345678')) self.assertEqual(btc_ticker.price_change_day_pct, Decimal('-2.33334567'))
def test_buy_coin_without_cash(self): profile = Profile.get_or_none(Profile.username == 'theusername') GameProfile.create(game=1, profile=profile, cash=0) GameCoin.create(game=1, coin=1) Ticker.create(coin=1, price=10, price_change_day_pct=1.1) res = self.client.post( '/game/1/coin', data=json.dumps({ 'coinId': '1', 'coinAmount': '1', }), content_type='application/json', headers={'Authorization': 'Bearer ' + self.token}) self.assertEqual(int(HTTPStatus.BAD_REQUEST), res._status_code)
def test_parsing_with_four_character_symbol(self): coin = Coin.create(name='foocoin', symbol='DASH') ping('DASH') dash = Coin.get(Coin.symbol == 'DASH') ticker = Ticker.get(Ticker.coin == dash) self.assertEqual(ticker.price, Decimal('56.12345678')) self.assertEqual(ticker.price_change_day_pct, Decimal('-1.23456789'))
def test_liquefy_success(self): profile = Profile.get_or_none(Profile.username == 'theusername') game_profile = GameProfile.create(game=1, profile=profile, cash=0) GameCoin.create(game=1, coin=1) GameProfileCoin.create(game_profile=game_profile, coin=1, coin_amount=2) Ticker.create( coin=1, price=10, captured_at=(datetime.utcnow()).isoformat(), price_change_day_pct=1.1, ) res = self.client.delete( '/game/1/coins', headers={'Authorization': 'Bearer ' + self.token}) self.assertEqual(int(HTTPStatus.OK), res._status_code)
def test_parsing_with_one_coin(self): ping('BTC') btc = Coin.get(Coin.symbol == 'BTC') btc_ticker = Ticker.get(Ticker.coin == btc) self.assertEqual(btc_ticker.price, Decimal('56.12345678')) self.assertEqual(btc_ticker.price_change_day_pct, Decimal('-2.33334567'))
def get_allowed_diff(self, sess, coin): if coin == 'BTC': return BTC_DIFF_THRESH now = datetime.datetime.utcnow() current = Ticker.current_ask(sess, coin, now) if current is None: current = 1 return BTC_DIFF_THRESH / current # in terms of the coin
def test_get_coins_success(self): profile = Profile.get_or_none(Profile.username == 'theusername') GameProfile.create(game=1, profile=profile, cash=10000) GameCoin.create( game=1, coin=1, ) for coin in Coin.select(): Ticker.create( coin=coin, price=30.0, captured_at=(datetime.utcnow()).isoformat(), price_change_day_pct=1.1, ) res = self.client.get( '/game/1/coins?timeSpan=1&sortBy=0&numPerPage=10&pageNum=1', headers={'Authorization': 'Bearer ' + self.token}) self.assertEqual(int(HTTPStatus.OK), res._status_code)
def account_value_btc(sess, account, now): # TODO: remove this duplicate btc = account.balance('BTC') for coin in account.coins: if coin == 'BTC': continue # BTC is priced in USD, everything else in BTC units = account.balance(coin) unit_price = Ticker.current_ask(sess, coin, now) btc += units * unit_price return btc
def close_alt_positions(sess, account, period): for coin in account.coins: if coin == 'BTC': continue price = Ticker.current_ask(sess, coin, period) assert price is not None, f"no price for {coin} at {period}" proceeds = account.trade(coin, -account.balance(coin), price, period) assert proceeds >= 0, f"got {proceeds} when selling {coin}" current = account.balance('BTC') assert current >= 0, f"negative BTC in account" current += proceeds account.balances['BTC'] = current
def save_historical_prices(tmp_file: IO) -> None: """Прочитать исторические данные из файла и сохранить их в базу. :param tmp_file: временный файл, откуда откуда будут прочитаны данные и сохранены в файл """ for chunk in chunked(tmp_file.readlines(), Config.CHUNK_SIZE): historical_prices = [json.loads(line) for line in chunk] symbols: Set[str] = set(historical_price['symbol'] for historical_price in historical_prices) Ticker.insert_tickers(symbols) symbol_to_uuid: Dict[str, UUID] = Ticker.get_uuids_by_symbol() HistoricalPrice.bulk_insert([ dict( ticker_id=symbol_to_uuid[historical_price['symbol']], **{k: v for k, v in historical_price.items() if k != 'symbol'}) for historical_price in historical_prices ]) session.commit()
def ping(*coins): res = requests.get(get_api_url(*coins)) tickers = [] for coin_res in res.json(): symbol = coin_res['symbol'] coin = Coin.get(Coin.symbol == symbol) price = Decimal(coin_res['price']) price_change_day_pct = Decimal(coin_res['1d']['price_change_pct']) ticker = Ticker.create(coin=coin, price=price, price_change_day_pct=price_change_day_pct) tickers.append(ticker) check_price_alerts(ticker) check_global_timed_game() return tickers
def test_sell_coin_success(self): profile = Profile.get_or_none(Profile.username == 'theusername') game_profile = GameProfile.create(game=1, profile=profile, cash=0) GameCoin.create(game=1, coin=1) GameProfileCoin.create(game_profile=game_profile, coin=1, coin_amount=2) Ticker.create( coin=1, price=10, captured_at=(datetime.utcnow()).isoformat(), price_change_day_pct=1.1, ) res = self.client.post( '/game/1/coin', data=json.dumps({ 'coinId': '1', 'coinAmount': '-1', }), content_type='application/json', headers={'Authorization': 'Bearer ' + self.token}) self.assertEqual(int(HTTPStatus.OK), res._status_code)
def __init__(self, sess, db_loc, step=timedelta(minutes=10)): self.db_loc = db_loc self.balances = {'BTC': 5} self.step = step self.coins = Ticker.coins(sess) self.start_data = fetch_data_timestamp(sess, oldest=True) self.end_data = fetch_data_timestamp(sess, oldest=False) # quiet other logs down to avoid spam default = logging.getLogger('default') default.setLevel(logging.ERROR) txns = logging.getLogger('txns') txns.setLevel(logging.ERROR)
def value_btc(self, sess, now=None): btc = self.balance('BTC') self.values_in_btc['BTC'] = btc for coin in self.coins: if coin == 'BTC': continue # BTC is priced in USD, everything else in BTC units = self.balance(coin) unit_price = Ticker.current_ask(sess, coin, now) if not unit_price: continue value = units * unit_price self.values_in_btc[coin] = value btc += value return btc
def get_pricing_by_coins(coins, start_time): coins_and_prices = [] for currentCoin in coins: prices_query = Ticker.select()\ .join(Coin)\ .where(Coin.id == currentCoin.id, Ticker.captured_at > start_time)\ .order_by(-Ticker.captured_at) if prices_query.count() == 0: raise BadRequest("Coin's prices not found") prices = [] for price in prices_query: prices.append(price) coins_and_prices.append({'coin': currentCoin, 'prices': prices}) return coins_and_prices
def calculate_strengths(self, now, ticker, allow_missing=False): if self.prices.get(ticker) is None: self.fetch_data(ticker, now) hour_avgs = self.avg_by_hour(now, ticker, allow_missing) if hour_avgs is None: return None log.debug("Averages by hour: {}".format(hour_avgs)) current_price = Ticker.current_ask(self.sess, ticker, now) if current_price is None or hour_avgs.get(1) is None: log.debug("No price for {} @ {}".format(ticker, now)) return None return current_price, [hour_avgs[hour] / current_price for hour in self.HOURS[1:]]
def update(sess, config): """"Pull data from the exchanges and store it in our database""" exchanges = { 'Bittrex': Bittrex(config), } start = datetime.datetime.utcnow() for name, exch in exchanges.items(): print(name) for coin in exch.COINS: data = exch.fetch_ticker(coin) if data is None: continue sess.add(Ticker(data)) sess.commit() elapsed = datetime.datetime.utcnow() - start if elapsed.seconds > 30: log.warn("Ran update in {}s".format(elapsed.seconds))
def sell_coin(coin_id, coin_amount, game_profile): gameProfileCoin = GameProfileCoin.get_or_none( GameProfileCoin.game_profile == game_profile.id, GameProfileCoin.coin == coin_id) if gameProfileCoin is None or gameProfileCoin.coin_amount < coin_amount: raise BadRequest('Not enough of these coins to sell') ticker = Ticker.select().where(Ticker.coin == coin_id).order_by( Ticker.captured_at.desc()) if ticker.count() == 0: raise BadRequest('Coin has no prices') ticker = ticker.get() new_cash = game_profile.cash + (ticker.price * coin_amount) new_coin_amount = gameProfileCoin.coin_amount - coin_amount GameProfileCoin.update(coin_amount=new_coin_amount).where( GameProfileCoin.id == gameProfileCoin.id).execute() GameProfile.update(cash=new_cash).where( GameProfile.id == game_profile.id).execute() return new_coin_amount
def get_net_worth_by_game_profile_id(game_profile_id): gameProfile = GameProfile.get_or_none(GameProfile.id == game_profile_id) if not gameProfile: raise BadRequest('User not in game') netWorth = gameProfile.cash gameProfileCoins = get_game_profile_coins_by_game_profile_id( game_profile_id) for gameProfileCoin in gameProfileCoins: ticker = Ticker.select().where( Ticker.coin == gameProfileCoin.coin).order_by( Ticker.captured_at.desc()) if ticker.count() == 0: raise BadRequest('One coin did not have prices') ticker = ticker.get() if not ticker: raise BadRequest('One coin did not exist') netWorth += ticker.price * gameProfileCoin.coin_amount return netWorth
def buy_and_hold(interval, coins, db_loc, step, balances): start, stop = interval account = Account(balances) # one share of each alt, one share of BTC btc_per_coin = account.balance('BTC') / (len(coins) + 1) with_fees = btc_per_coin - (btc_per_coin * 0.0025) db = create_db(db_loc) sess = new_session(db) for coin in coins: price = Ticker.current_ask(sess, coin, now=start) if not price: continue to_buy = with_fees / price cost = account.trade(coin, to_buy, price, start) account.update('BTC', cost, start) start_value = account_value_btc(sess, account, now=start) finish_value = account_value_btc(sess, account, stop) low = min(start_value, finish_value) high = max(start_value, finish_value) results = BacktestResult(start, stop, step, start_value, finish_value, account.fees, account.txns, gain_txns=[], loss_txns=[], out_of_btc=0, hit_coin_limit=0, high=high, low=low) log.debug("\nBuy and hold\n") results.print_results() return results
def buy_coin(coin_id, coin_amount, game_profile): ticker = Ticker.select().where(Ticker.coin == coin_id).order_by( Ticker.captured_at.desc()) if ticker.count() == 0: raise BadRequest('Coin has no prices') ticker = ticker.get() new_cash = game_profile.cash - (ticker.price * coin_amount) if new_cash < 0: raise BadRequest('Not enough cash to buy this coin amount') game_coin = GameCoin.get_or_none(GameCoin.game == game_profile.game, GameCoin.coin == coin_id) if game_coin is None: raise BadRequest('Coin does not exist in this game') gameProfileCoin = GameProfileCoin.get_or_none( GameProfileCoin.game_profile == game_profile.id, GameProfileCoin.coin == coin_id) if gameProfileCoin is None: GameProfileCoin.create(game_profile=game_profile.id, coin=coin_id, coin_amount=coin_amount) rows = GameProfile.update(cash=new_cash).where( GameProfile == game_profile).execute() if rows == 0: raise BadRequest('Money could not be removed from your account') return coin_amount else: new_coin_amount = gameProfileCoin.coin_amount + coin_amount rows = GameProfile.update(cash=new_cash).where( GameProfile == game_profile).execute() if rows == 0: raise BadRequest('Money could not be removed from your account') GameProfileCoin.update(coin_amount=new_coin_amount).where( GameProfileCoin.id == gameProfileCoin.id).execute() return new_coin_amount
def get_ticker_price_by_coin_id(coin_id): return Ticker.select(Ticker.price).where(coin_id == Ticker.coin)
def from_db(sess, name, exchange, ccxt): if not name or not exchange: raise ValueError("Must specify acount name and exchange") balances, opens = DurableAccount.fetch_balances(sess, name, exchange) coins = Ticker.coins(sess) return DurableAccount(name, exchange, ccxt, balances, opens, coins)
def get_tickers_24hr(): yesterday = datetime.utcnow() - timedelta(days=1) tickers = Ticker.select().where(Ticker.captured_at > yesterday) if not tickers: return [] return tickers
def test_parsing_with_no_coins(self): num_tickers_before = Ticker.select().count() ping() num_tickers_after = Ticker.select().count() self.assertEqual(num_tickers_before, num_tickers_after)