コード例 #1
0
def buy(candle, criteria):
    """Create or update existing position for zscore above threshold value.
    """
    global client
    orderbook = client.get_orderbook_ticker(symbol=candle['pair'])

    return get_db().trades.insert_one(
        odict({
            'pair':
            candle['pair'],
            'freq':
            candle['freq'],
            'status':
            'open',
            'start_time':
            now(),
            'strategy':
            criteria['snapshot']['strategy'],
            'snapshots': [criteria['snapshot']],
            'orders': [
                odict({
                    'action': 'BUY',
                    'ex': 'Binance',
                    'time': now(),
                    'price':
                    candle['close'],  # np.float64(orderbook['askPrice']),
                    'volume': 1.0,
                    'quote': BINANCE['TRADE_AMT'],
                    'fee': BINANCE['TRADE_AMT'] * (BINANCE['PCT_FEE'] / 100),
                    'orderbook': orderbook,
                    'candle': candle
                })
            ]
        })).inserted_id
コード例 #2
0
class User(mongo.Document):
    """ User model """
    # pylint: disable=E1101
    username   = mongo.StringField(required=True, unique=True)
    password   = mongo.StringField(required=True)
    email      = mongo.EmailField(required=True, unique=True)
    first_name = mongo.StringField(max_length=50)
    last_name  = mongo.StringField(max_length=50)
    created_at = mongo.IntField(default=now())
    updated_at = mongo.IntField(default=now())
    # pylint: enable=E1101

    @classmethod
    # pylint: disable=W0613
    def pre_save(cls, sender, document, **kwargs):
        """
        Before save, lower case the username, enail
        and hash the password
        """
        # pylint: enable=W0613
        document.username = document.username.lower()
        document.email    = document.email.lower()
        document.password = hash_password(document.password)

    meta = {
        'indexes': ['username', 'email', '-created_at']
    }
コード例 #3
0
ファイル: trade.py プロジェクト: SeanEstey/coincruncher
def buy(ss, algo):
    """Open new position, insert record to DB.
    @ss: snapshot dict
    @algo: algorithm definition dict
    """
    db, client = app.db, app.bot.client

    if ss['book'] is None:
        book = odict(client.get_orderbook_ticker(symbol=ss['pair']))
        del book['symbol']
        [book.update({k: np.float64(v)}) for k, v in book.items()]
        ss['book'] = book

    record = odict({
        'pair':
        ss['pair'],
        'quote_asset':
        db.assets.find_one({'symbol': ss['pair']})['quoteAsset'],
        'freqstr':
        ss['candle']['freqstr'],
        'status':
        'open',
        'start_time':
        now(),
        'algo':
        algo['name'],
        'stoploss':
        algo['stoploss'],
        'snapshots': [ss],
        'stats': {},
        'details': [{
            'algo': algo['name'],
            'section': 'entry',
            'desc': algo_to_string(algo['name'], 'entry')
        }],
        'orders': [
            odict({
                'action': 'BUY',
                'ex': 'Binance',
                'time': now(),
                'price': ss['book']['askPrice'],
                'volume': 1.0,
                'quote': TRD_AMT_MAX,
                'fee': TRD_AMT_MAX * (BINANCE_PCT_FEE / 100)
            })
        ]
    })

    result = db.trades.insert_one(record)
    update_stats(record, ss)

    lock.acquire()
    print("BUY {} ({})".format(ss['pair'], algo['name']))
    lock.release()

    return result.inserted_id
コード例 #4
0
ファイル: candles.py プロジェクト: HAMMERjah/coincruncher
def query_api(pair, freq, start=None, end=None, force=False):
    """Get Historical Klines (candles) from Binance.
    @freq: Binance kline frequency:
        1m, 3m, 5m, 15m, 30m, 1h, 2h, 4h, 6h, 8h, 12h, 1d, 3d, 1w, 1M]
        m -> minutes; h -> hours; d -> days; w -> weeks; M -> months
    @force: if False, only query unstored data (faster). If True, query all.
    Return: list of OHLCV value
    """
    t1 = Timer()
    limit = 500
    idx = 0
    results = []
    periodlen = intrvl_to_ms(freq)
    end_ts = datestr_to_ms(end) if end else dt_to_ms(now())
    start_ts = datestr_to_ms(start) if start else end_ts - (periodlen * 20)

    # Skip queries for records already stored
    if force == False:
        query = {"pair":pair, "freq":freq}
        if start:
            query["open_time"] = {"$gt": datestr_to_dt(start)}

        newer = app.get_db().candles.find(query).sort('open_time',-1).limit(1)

        if newer.count() > 0:
            dt = list(newer)[0]['open_time']
            start_ts = int(dt.timestamp()*1000 + periodlen)

            if start_ts > end_ts:
                log.debug("All records for %s already stored.", pair)
                return []

    client = Client("", "")

    #while len(results) < 500 and start_ts < end_ts:
    while start_ts < end_ts:
        try:
            data = client.get_klines(symbol=pair, interval=freq,
                limit=limit, startTime=start_ts, endTime=end_ts)

            if len(data) == 0:
                start_ts += periodlen
            else:
                # Don't want candles that aren't closed yet
                if data[-1][6] >= dt_to_ms(now()):
                    results += data[:-1]
                    break
                results += data
                start_ts = data[-1][0] + periodlen
        except Exception as e:
            log.exception("Binance API request error. e=%s", str(e))

    log.debug('%s %s %s queried [%ss].', len(results), freq, pair,
        t1.elapsed(unit='s'))
    return results
コード例 #5
0
def sell(doc, candle, orderbook=None, criteria=None):
    """Close off existing position and calculate earnings.
    """
    global client
    ob = orderbook if orderbook else client.get_orderbook_ticker(
        symbol=candle['pair'])
    bid = np.float64(ob['bidPrice'])

    pct_fee = BINANCE['PCT_FEE']
    buy_vol = np.float64(doc['orders'][0]['volume'])
    buy_quote = np.float64(doc['orders'][0]['quote'])
    p1 = np.float64(doc['orders'][0]['price'])

    pct_gain = pct_diff(p1, candle['close'])
    quote = buy_quote * (1 - pct_fee / 100)
    fee = (bid * buy_vol) * (pct_fee / 100)
    pct_net_gain = net_earn = pct_gain - (pct_fee * 2)  #quote - buy_quote

    duration = now() - doc['start_time']
    candle['buy_ratio'] = candle['buy_ratio'].round(4)

    get_db().trades.update_one({'_id': doc['_id']}, {
        '$push': {
            'snapshots': criteria['snapshot']
        },
        '$push': {
            'orders':
            odict({
                'action': 'SELL',
                'ex': 'Binance',
                'time': now(),
                'price': candle['close'],
                'volume': 1.0,
                'quote': buy_quote,
                'fee': fee,
                'orderbook': ob,
                'candle': candle,
            })
        },
        '$set': {
            'status': 'closed',
            'end_time': now(),
            'duration': int(duration.total_seconds()),
            'pct_gain': pct_gain.round(4),
            'pct_net_gain': pct_net_gain.round(4),
        }
    })
    return doc['_id']
コード例 #6
0
def trades(trade_ids):
    db = app.get_db()
    cols = [
        'freq', "type", "Δprice", "macd", "rsi", "zscore", "time", "algo",
        "details"
    ]
    data, indexes = [], []

    for _id in trade_ids:
        record = db.trades.find_one({"_id": _id})
        indexes.append(record['pair'])
        ss1 = record['snapshots'][0]
        ss_new = record['snapshots'][-1]
        df = app.bot.dfc.loc[record['pair'],
                             strtofreq(record['freqstr'])].tail(100)

        if len(record['orders']) > 1:
            c1 = ss1['candle']
            c2 = ss_new['candle']
            data.append([
                c2['freqstr'], 'SELL',
                pct_diff(c1['close'],
                         c2['close']), ss_new['indicators']['macd']['value'],
                ss_new['indicators']['rsi'], ss_new['indicators']['zscore'],
                to_relative_str(now() - record['start_time']), record['algo'],
                record['details'][-1]['section'].title()
            ])
        # Buy trade
        else:
            c1 = ss1['candle']
            data.append([
                c1['freqstr'], 'BUY', 0.0,
                ss_new['indicators']['macd']['value'],
                ss_new['indicators']['rsi'], ss_new['indicators']['zscore'],
                "-", record['algo'], record['details'][-1]['section'].title()
            ])

    if len(data) == 0:
        return tradelog("0 trades executed")

    df = pd.DataFrame(data, index=pd.Index(indexes), columns=cols)
    df = df[cols]
    lines = df.to_string(
        formatters={
            cols[0]: ' {}'.format,
            cols[1]: ' {}'.format,
            cols[2]: ' {:+.2f}%'.format,
            cols[3]: ' {:+.3f}'.format,
            cols[4]: '{:.0f}'.format,
            cols[5]: '{}'.format,
            cols[5]: '{}'.format,
            cols[6]: '{}'.format
        }).split("\n")

    tradelog('-' * TRADELOG_WIDTH)
    tradelog("{} trade(s) executed:".format(len(df)))
    [tradelog(line) for line in lines]
コード例 #7
0
 def put(self, user_id):
     """
     :param user_id: The user id
     """
     user = User.objects.get_or_404(id=user_id)
     form = UpdateUserForm()
     if form.validate_on_submit():
         user.username   = form.username.data
         user.email      = form.email.data
         user.first_name = form.first_name.data
         user.last_name  = form.last_name.data
         user.updated_at = now()
         user.save()
         return make_api_response(user_schema, user)
     raise ValidationError(form.errors)
コード例 #8
0
ファイル: strategy.py プロジェクト: HAMMERjah/coincruncher
def snapshot(candle):
    z = signals.z_score(candle, rules['z-score']['periods']).to_dict()
    client = Client("", "")
    ob = client.get_orderbook_ticker(symbol=candle['pair'])

    macd_desc = macd.describe(candle)
    phase = macd_desc['phase']
    # Convert datetime index to str for mongodb storage.
    phase.index = [str(n)[:-10] for n in phase.index.values]
    last = phase.iloc[-1]

    return odict({
        'time':
        now(),
        'strategy':
        None,
        'details':
        macd_desc['details'],
        'price':
        odict({
            'close':
            candle['close'],
            'z-score':
            round(z['close'], 2),
            'emaDiff':
            signals.ema_pct_change(candle, rules['ema']['span']).iloc[-1],
            'ask':
            float(ob['askPrice']),
            'bid':
            float(ob['bidPrice'])
        }),
        'volume':
        odict({
            'value': candle['volume'],
            'z-score': round(z['volume'], 2),
        }),
        'buyRatio':
        odict({
            'value': round(candle['buy_ratio'], 2),
            'z-score': round(z['buy_ratio'], 2),
        }),
        'macd':
        odict({
            'value': last.round(10),
            'phase': phase.round(10).to_dict(odict),
            'desc': phase.describe().round(10).to_dict()
        })
    })
コード例 #9
0
ファイル: printer.py プロジェクト: HAMMERjah/coincruncher
def new_trades(trade_ids):
    db = app.get_db()
    dfc = app.bot.dfc
    cols = ["Type", "ΔPrice", "Slope", "Z-Score", "ΔZ-Score", "Time"]
    data, indexes = [], []

    for _id in trade_ids:
        record = db.trades.find_one({"_id": _id})
        freq_str = record['orders'][0]['candle']['freq']
        indexes.append(record['pair'])
        candle = candles.newest(record['pair'], freq_str, df=dfc)
        ss1 = record['snapshots'][0]
        ss2 = record['snapshots'][-1]

        if len(record['orders']) > 1:
            c1 = record['orders'][0]['candle']
            data.append([
                'SELL',
                pct_diff(c1['close'], candle['close']),
                ss2['price']['emaDiff'], ss2['price']['z-score'],
                ss2['price']['z-score'] - ss1['price']['z-score'],
                to_relative_str(now() - record['start_time'])
            ])
        # Buy trade
        else:
            data.append([
                'BUY', 0.0, ss2['price']['emaDiff'], ss1['price']['z-score'],
                0.0, "-"
            ])

    if len(data) == 0:
        return tradelog("0 trades executed")

    df = pd.DataFrame(data, index=pd.Index(indexes), columns=cols)
    df = df[cols]
    lines = df.to_string(
        formatters={
            cols[0]: ' {}'.format,
            cols[1]: ' {:+.2f}%'.format,
            cols[2]: ' {:+.2f}'.format,
            cols[3]: ' {:+.2f}'.format,
            cols[4]: ' {:+.2f}'.format,
            cols[5]: '{}'.format
        }).split("\n")
    tradelog("{} trade(s) executed:".format(len(df)))
    [tradelog(line) for line in lines]
コード例 #10
0
def positions():
    """Position summary.
    """
    db = app.get_db()
    cols = ["freq", "price", "Δprice", "macd", "rsi", "zscore", "time", "algo"]
    data, indexes = [], []
    opentrades = db.trades.find({'status': 'open'})

    for record in opentrades:
        ss1 = record['snapshots'][0]
        c1 = ss1['candle']
        ss_new = record['snapshots'][-1]
        freq = strtofreq(record['freqstr'])
        df = app.bot.dfc.loc[record['pair'], freq]
        dfmacd, phases = macd.histo_phases(df, record['pair'],
                                           record['freqstr'], 100)

        data.append([
            c1['freqstr'], df.iloc[-1]['close'],
            pct_diff(c1['close'], df.iloc[-1]['close']), phases[-1].iloc[-1],
            signals.rsi(df['close'], 14),
            signals.zscore(df['close'], df.iloc[-1]['close'], 21),
            to_relative_str(now() - record['start_time']), record['algo']
        ])
        indexes.append(record['pair'])

    if opentrades.count() == 0:
        tradelog("0 open positions")
    else:
        df = pd.DataFrame(data, index=pd.Index(indexes), columns=cols)
        df = df[cols]
        lines = df.to_string(
            formatters={
                cols[0]: ' {}'.format,
                cols[1]: ' {:g}'.format,
                cols[2]: ' {:+.2f}%'.format,
                cols[3]: '  {:+.3f}'.format,
                cols[4]: '{:.0f}'.format,
                cols[5]: '{}'.format,
                cols[6]: ' {}'.format
            }).split("\n")
        tradelog('-' * TRADELOG_WIDTH)
        tradelog("{} position(s):".format(len(df)))
        [tradelog(line) for line in lines]
        return df
コード例 #11
0
ファイル: candles.py プロジェクト: HAMMERjah/coincruncher
def merge_new(dfc, pairs, span=None):
    """Merge only newly updated DB records into dataframe to avoid ~150k
    DB reads every main loop.
    """
    global last_update
    t1 = Timer()
    columns = ['open', 'close', 'trades', 'volume', 'buy_ratio']
    exclude = ['_id','high','low','quote_vol','sell_vol', 'close_time']
    projection = dict(zip(exclude, [False]*len(exclude)))
    idx, data = [], []
    db = app.get_db()

    if span is None and last_update:
        # If no span, query/merge db records inserted since last update.
        oid = ObjectId.from_datetime(last_update)
        last_update = now()
        _filter = {'_id':{'$gte':oid}}
    else:
        # Else query/merge all since timespan.
        span = span if span else timedelta(days=7)
        last_update = now()
        _filter = {'pair':{'$in':pairs}, 'close_time':{'$gte':now()-span}}

    batches = db.candles.find_raw_batches(_filter, projection)

    if batches.count() < 1:
        return dfc

    try:
        ndarray = bsonnumpy.sequence_to_ndarray(
            batches,
            dtype,
            db.candles.count()
        )
    except Exception as e:
        log.error(str(e))
        return dfc
        #raise

    df = pd.DataFrame(ndarray)
    df['open_time'] = pd.to_datetime(df['open_time'], unit='ms')
    df['freq'] = df['freq'].str.decode('utf-8')
    df['pair'] = df['pair'].str.decode('utf-8')

    df['freq'] = df['freq'].replace('1m',60)
    df['freq'] = df['freq'].replace('5m',300)
    df['freq'] = df['freq'].replace('1h',3600)
    df['freq'] = df['freq'].replace('1d',86400)
    df = df.sort_values(by=['pair','freq','open_time'])

    df2 = pd.DataFrame(df[columns].values,
        index = pd.MultiIndex.from_arrays(
            [df['pair'], df['freq'], df['open_time']],
            names = ['pair','freq','open_time']),
        columns = columns
    ).sort_index()

    df3 = pd.concat([dfc, df2]).drop_duplicates().sort_index()

    log.debug("{:,} records loaded into numpy. [{:,.1f} ms]".format(
        len(df3), t1))
    #print("Done in %s ms" % t1)
    return df3
コード例 #12
0
def aggregate_mkt(freqstr=None):
    try:
        dfT = binance_24h()
    except Exception as e:
        lock.acquire()
        return print("Binance client error. {}".format(str(e)))
        lock.release()

    dfV = pd.DataFrame(
        dfT.groupby('quoteAsset').apply(lambda x: x['quoteVol'].sum()),
        columns=['volume'])

    summaries = []
    for idx, row in dfV.iterrows():
        summaries.append(summarize(dfT, idx))

    dfA = pd.DataFrame(summaries, index=[n['symbol'] for n in summaries])
    dfA = dfA[['pairs', '24hPriceChange', '24hAggVol']]\
        .round(2).sort_values('24hAggVol')

    # Diff in both 24h_delta_price's is freq price change.
    formatters = {}
    k = None
    if freqstr:
        db = app.get_db()
        last = list(
            db.tickers.find({
                'freqstr': freqstr
            }, {
                '_id': 0,
                'freqstr': 0,
                'ex': 0,
                'time': 0
            }).sort('time', -1))
        db.tickers.insert_one({
            **{
                'ex': 'Binance',
                'time': now(),
                'freqstr': freqstr
            },
            **dfA.to_dict('index')
        })

        if len(last) > 0:
            k = '{}.Δprice'.format(freqstr)
            dfA[k] = dfA['24hPriceChange'] - pd.DataFrame(
                last[0]).T['24hPriceChange']
            formatters[k] = '   {:+.2f}%'.format

    formatters.update({
        '24h.Δprice': '   {:+.2f}%'.format,
        '24h.agg.vol': '   {:,.0f}'.format
    })
    scanlog("Aggregate Markets")
    columns = ['pairs', k, '24h.Δprice', '24h.agg.vol']
    if not k:
        columns = [n for n in columns if n]
    _cols = dfA.columns.tolist()
    dfA.columns = columns
    lines = dfA.to_string(columns=columns, formatters=formatters).split("\n")
    [scanlog(line) for line in lines]
    scanlog("")
    dfA.columns = _cols
    return dfA
コード例 #13
0
def update(_freq_str):
    """Evaluate Binance market data and execute buy/sell trades.
    """
    global n_cycles, freq_str, freq

    trade_ids = []
    freq_str = _freq_str
    freq = strtofreq[freq_str]
    t1 = Timer()
    db = get_db()

    # Update candles updated by websocket
    app.bot.dfc = candles.merge_new(app.bot.dfc, pairs, span=None)

    tradelog('*' * 80)
    duration = to_relative_str(now() - start)
    hdr = "Cycle #{}, Period {} {:>%s}" % (61 - len(str(n_cycles)))
    tradelog(hdr.format(n_cycles, freq_str, duration))
    tradelog('*' * 80)

    # Output candle signals to siglog
    if freq_str in siglog_freq:
        siglog('-' * 80)
        for pair in pairs:
            printer.candle_sig(candles.newest(pair, freq_str, df=app.bot.dfc))

    # Evaluate existing positions
    active = list(db.trades.find({'status': 'open', 'freq': freq_str}))

    for trade in active:
        candle = candles.newest(trade['pair'], freq_str, df=app.bot.dfc)
        result = strategy.update(candle, trade)

        print('{} {} {}'.format(candle['pair'], candle['freq'],
                                result['snapshot']['details']))

        if result['action'] == 'SELL':
            trade_ids += [sell(trade, candle, criteria=result)]
        else:
            db.trades.update_one({"_id": trade["_id"]},
                                 {"$push": {
                                     "snapshots": result['snapshot']
                                 }})

    # Inverse active list and evaluate opening new positions
    inactive = sorted(list(set(pairs) - set([n['pair'] for n in active])))

    for pair in inactive:
        candle = candles.newest(pair, freq_str, df=app.bot.dfc)
        results = strategy.evaluate(candle)
        for res in results:
            print('{} {} {}'.format(candle['pair'], candle['freq'],
                                    res['snapshot']['details']))

            if res['action'] == 'BUY':
                trade_ids += [buy(candle, criteria=res)]

    tradelog('-' * 80)
    printer.new_trades([n for n in trade_ids if n])
    tradelog('-' * 80)
    printer.positions('open')
    tradelog('-' * 80)
    printer.positions('closed')

    n_cycles += 1
コード例 #14
0
ファイル: printer.py プロジェクト: HAMMERjah/coincruncher
def positions(_type):
    """Position summary.
    @_type: 'open', 'closed'
    @start: datetime.datetime for closed trades
    """
    from docs.rules import TRADING_PAIRS as pairs
    db = app.get_db()
    dfc = app.bot.dfc

    if _type == 'open':
        cols = ["ΔPrice", "Slope", " Z-Score", " ΔZ-Score", "Macd", "Time"]
        data, indexes = [], []

        _trades = list(
            db.trades.find({
                'status': 'open',
                'pair': {
                    "$in": pairs
                }
            }))

        for record in _trades:
            c1 = record['orders'][0]['candle']
            c2 = candles.newest(record['pair'], c1['freq'], df=dfc)
            ss1 = record['snapshots'][0]
            ss2 = record['snapshots'][-1]

            data.append([
                pct_diff(c1['close'], c2['close']), ss2['price']['emaDiff'],
                ss2['price']['z-score'],
                ss2['price']['z-score'] - ss1['price']['z-score'],
                ss2['macd']['value'],
                to_relative_str(now() - record['start_time'])
            ])
            indexes.append(record['pair'])

        if len(_trades) == 0:
            tradelog("0 open positions")
        else:
            df = pd.DataFrame(data, index=pd.Index(indexes), columns=cols)
            df = df[cols]
            lines = df.to_string(
                formatters={
                    cols[0]: ' {:+.2f}%'.format,
                    cols[1]: ' {:+.2f}%'.format,
                    cols[2]: '  {:.2f}'.format,
                    cols[3]: '  {:+.2f}'.format,
                    cols[4]: '  {:+.2f}'.format,
                    cols[5]: '{}'.format
                }).split("\n")
            tradelog("{} position(s):".format(len(df)))
            [tradelog(line) for line in lines]
            return df
    elif _type == 'closed':
        if datetime.now().time().hour >= 8:
            start = dateparser.parse("8 am today").replace(
                tzinfo=tzlocal.get_localzone()).astimezone(pytz.utc)
        else:
            start = dateparser.parse("8 am yesterday").replace(
                tzinfo=tzlocal.get_localzone()).astimezone(pytz.utc)

        closed = list(
            db.trades.find({
                'status': 'closed',
                'end_time': {
                    '$gte': start
                }
            }))

        #print("%s trades today ending after %s" % (len(closed), start))

        n_win, pct_net_gain = 0, 0
        for n in closed:
            if n['pct_net_gain'] > 0:
                n_win += 1
            pct_net_gain += n['pct_net_gain']

        ratio = (n_win / len(closed)) * 100 if len(closed) > 0 else 0

        tradelog("{} of {} trade(s) today were profitable.".format(
            n_win, len(closed)))
        duration = to_relative_str(now() - start)
        tradelog("{:+.2f}% net profit today.".format(pct_net_gain))
コード例 #15
0

def tradelog(msg):
    log.log(99, msg)


def siglog(msg):
    log.log(100, msg)


log = logging.getLogger('trade')

# GLOBALS
siglog_freq = ['5m', '1h', '1d']
n_cycles = 0
start = now()
freq = None
freq_str = None
client = None


#------------------------------------------------------------------------------
def init():
    """Preload candles records from mongoDB to global dataframe.
    Performance: ~3,000ms/100k records
    """
    t1 = Timer()
    log.info('Preloading historic data...')

    span = delta(days=7)
    app.bot.dfc = candles.merge_new(pd.DataFrame(), pairs, span=span)
コード例 #16
0
ファイル: trade.py プロジェクト: SeanEstey/coincruncher
def sell(trade, ss, section):
    """Close off existing position and calculate earnings.
    @trade: db trade document dict
    @ss: snapshot dict
    @section: key name of evaluated algo conditions
    """
    db, client = app.db, app.bot.client

    # Algorithm criteria details
    algo = [n for n in TRD_ALGOS \
        if n['name'] == trade['algo']][0]
    details = {'name': algo['name'], 'section': section}
    if section == 'stoploss':
        details.update({'desc': algo['stoploss']})
    else:
        details.update({'desc': algo_to_string(algo['name'], section)})

    # Get orderbook if not already stored in snapshot.
    if ss['book'] is None:
        try:
            book = odict(client.get_orderbook_ticker(symbol=trade['pair']))
        except (BinanceRequestException, ConnectionError) as e:
            log.debug(str(e))
            lock.acquire()
            print("Error acquiring orderbook. Sell failed.")
            lock.release()
            return []

        del book['symbol']
        [book.update({k: np.float64(v)}) for k, v in book.items()]
        ss['book'] = book

    # Profit/loss calculations.
    pct_fee = BINANCE_PCT_FEE
    bid = ss['book']['bidPrice']
    ask = ss['book']['askPrice']
    buy_vol = np.float64(trade['orders'][0]['volume'])
    buy_quote = np.float64(trade['orders'][0]['quote'])
    p1 = np.float64(trade['orders'][0]['price'])

    pct_gain = pct_diff(p1, bid)
    quote = buy_quote * (1 - pct_fee / 100)
    fee = (bid * buy_vol) * (pct_fee / 100)
    pct_net_gain = net_earn = pct_gain - (pct_fee * 2)
    duration = now() - trade['start_time']

    db.trades.update_one({'_id': trade['_id']}, {
        '$push': {
            'snapshots':
            ss,
            'details':
            details,
            'orders':
            odict({
                'action': 'SELL',
                'ex': 'Binance',
                'time': now(),
                'price': bid,
                'volume': 1.0,
                'quote': buy_quote,
                'fee': fee
            })
        },
        '$set': {
            'status': 'closed',
            'end_time': now(),
            'duration': int(duration.total_seconds()),
            'pct_gain': pct_gain.round(4),
            'pct_net_gain': pct_net_gain.round(4)
        }
    })

    lock.acquire()
    print("SELL {} ({}) Details: {}. {}"\
        .format(trade['pair'], details['name'],
            details['section'].title(), details['desc']))
    lock.release()
    return trade['_id']
コード例 #17
0
ファイル: trade.py プロジェクト: SeanEstey/coincruncher
def snapshot(c):
    """Gather state of trade--candle, indicators--each tick and save to DB.
    """
    global dfW
    book = None
    wick_slope = macd_value = amp_slope = np.nan
    pair, freqstr = c['pair'], c['freqstr']

    buyratio = (c['buy_vol'] / c['volume']) if c['volume'] > 0 else 0.0

    # MACD Indicators
    dfm_dict = {}
    df = app.bot.dfc.loc[pair, strtofreq(freqstr)]

    try:
        dfmacd, phases = macd.histo_phases(df,
                                           pair,
                                           freqstr,
                                           100,
                                           to_bson=True)
    except Exception as e:
        lock.acquire()
        print('snapshot exc')
        print(str(e))
        lock.release()

    if len(dfmacd) < 1:
        dfm_dict['bars'] = 0
    else:
        dfm_dict = dfmacd.iloc[-1].to_dict()
        dfm_dict['bars'] = int(dfm_dict['bars'])
        macd_value = phases[-1].iloc[-1]
        amp_slope = phases[-1].diff().ewm(span=min(3, len(phases[-1])),
                                          min_periods=0).mean().iloc[-1]

    if c['closed']:
        # Find price EMA WITHIN the wick (i.e. each trade). Very
        # small movements.
        #prices = dfW.loc[c['pair'], c['freqstr']]['close']
        #wick_slope = prices.diff().ewm(span=len(prices)).mean().iloc[-1]
        # FIXME
        wick_slope = 0.0

    return {
        'pair': pair,
        'time': now(),
        'book': None,
        'candle': c,
        'indicators': {
            'buyRatio': round(buyratio, 2),
            'rsi': signals.rsi(df['close'].tail(100), 14),
            'wickSlope': wick_slope,
            'zscore': signals.zscore(df['close'], c['close'], 21),
            'macd': {
                **dfm_dict,
                **{
                    'ampSlope': round(amp_slope, 2),
                    'value': round(macd_value, 2)
                }
            }
        }
    }
コード例 #18
0
ファイル: macd.py プロジェクト: HAMMERjah/coincruncher
def plot(pair, units, n_units, n_periods):
    from dateparser import parse
    import plotly.offline as offline
    import plotly.tools as tools, plotly.graph_objs as go
    from app.common.utils import utc_datetime as now
    from . import candles

    freqstr = ('%s%s'%(n_units, units[1]), '%s%s'%(n_units, units[2]))
    freq = strtofreq[freqstr[0]]
    start_str = "{} {} ago utc".format((n_periods + 75) * n_units, units[0])

    candles.update([pair], freqstr[0],
        start=start_str, force=True)
    app.bot.dfc = candles.merge_new(pd.DataFrame(), [pair],
        span=now()-parse(start_str))
    df_macd = generate(app.bot.dfc.loc[pair, freq])
    scan = agg_describe(pair, freqstr[0], n_periods, pdfreqstr=freqstr[1])
    scan['summary'] = scan['summary'].replace("\n", "<br>")

    # Stacked Subplots with a Shared X-Axis
    t1 = go.Scatter(
        x=df_macd.index,
        y=df_macd['close'],
        name="Price")
    t2 = go.Bar(
        x=df_macd.index,
        y=df_macd['macd_diff'],
        name="MACD_diff (normalized)",
        yaxis='y2')
    t3 = go.Bar(
        x=df_macd.index,
        y=df_macd['volume'],
        name="Volume",
        yaxis='y3')
    data = [t1, t2, t3]

    layout = go.Layout(
        title='{} MACD'.format(pair),
        yaxis=dict(
            domain=[0.4, 1]
        ),
        yaxis2=dict(
            domain=[0.2, 0.4]
        ),
        yaxis3=dict(
            domain=[0, 0.2]
        ),
        xaxis=dict(
            anchor = "y3",
            #domain=[0.0, 0.1],
            title="<BR>" + scan['summary']
        ),
        margin = dict(l=100, r=100, b=400, t=75, pad=25)
        #fig['layout']['xaxis1'].update(titlefont=dict(
        #    family='Arial, sans-serif',
        #    size=18,
        #    color='grey'
        #))
    )

    fig = go.Figure(data=data, layout=layout)
    return fig

    """fig = tools.make_subplots(
コード例 #19
0
ファイル: scanner.py プロジェクト: HAMMERjah/coincruncher
def indicators(df):
    freq_str, freq = '1h', 3600
    start_str, periods = '48 hours ago utc', 48

    top = df.sort_values('close - open')  #.tail(n)

    _df = pd.DataFrame(columns=[
        '∆(Close - Open)',
        '∆(High - Low)',
        'SD(∆P)',
        'Σ(∆P > 0)',
        'μ(∆P > 0)',
        'Σ(∆P < 0)',
        'μ(∆P < 0)',
        '+MACD/-MACD',
        '∆ema',
        'BuyVol',
    ],
                       index=top.index).astype('float64')

    for pair, row in df.iterrows():
        # API query/load candle data
        candles.update([pair], freq_str, start=start, force=True)
        app.bot.dfc = candles.merge_new(app.bot.dfc, [pair],
                                        span=now() - parse(start_str))
        dfc = app.bot.dfc.loc[pair].xs(freq, level=0).tail(periods)
        candle = candles.newest(pair, freq_str, df=app.bot.dfc)

        # Calc indicators
        pct_close_std = np.float64(
            dfc['close'].pct_change().describe()['std'] * 100)
        br_mean = dfc['buy_ratio'].describe()['mean'] * 100
        ema_slope = signals.ema_pct_change(candle,
                                           strats['ema']['span']).iloc[-1]

        # Price movement (total, velocity, momentum)
        pdelta = dfc['close'].tail(24).pct_change() * 100
        pos_pdelta = pdelta[pdelta > 0]
        neg_pdelta = pdelta[pdelta < 0]
        macd = signals.macd(dfc, strats['macd']['fast_span'],
                            strats['macd']['slow_span'])
        pos_mom = macd[macd['macd_diff'] > 0]['macd_diff'].sum()
        neg_mom = abs(macd[macd['macd_diff'] < 0]['macd_diff'].sum())
        mom_ratio = pos_mom / neg_mom

        # MACD Histogram analysis.
        # Iterate through macd_diff, group histograms, calc avg length/depth

        _df.loc[pair] = [
            # done already,
            # done already,
            pct_close_std,
            p_up.sum(),
            p_up.mean(),
            p_down.sum(),
            p_down.mean(),
            mom_ratio,
            br_mean,
            ema_slope,
        ]
    return _df
コード例 #20
0
ファイル: strategy.py プロジェクト: HAMMERjah/coincruncher
def _momentum(candle, record=None):
    """Evaluate positive momentum for buy/sell decision.
    Buy: on confirmation of price/volume.
    Sell: at peak price slope.
    FIXME: confirm high volume across multiple 1m candles, not just 1.
    """
    if candle['freq'] != '5m':
        return None

    periods = rules['z-score']['periods']
    z = signals.z_score(candle, periods)
    ema = signals.ema_pct_change(candle)

    client = Client("", "")
    ob = client.get_orderbook_ticker(symbol=candle['pair'])

    snapshot = {
        'time': now(),
        'candle_price': candle['close'],
        'volume': candle['volume'],
        'buy_ratio': candle['buy_ratio'],
        'z-score': z.to_dict(),
        'ema_pct_change': ema.iloc[-1]
    }

    if record is None:
        if (ema.tail(5) < 0).any():
            print('ema.tail(5).any() < 0')
            return False
        if z.volume < 2.0 or z.buy_ratio < 0.5:
            print('z.volume < 2.0 or z.buy_ratio < 0.5')
            return False

        return {
            'strategy': 'momentum',
            'snapshot': {
                **snapshot,
                **{
                    'ask_price': float(ob['askPrice'])
                }
            },
            'details': {
                'ema:slope:tail(5):min > thresh':
                '{:+.2f}% > 0'.format(ema.tail(5).min()),
                'z-score:volume > thresh':
                '{:+.2f} > 0.5'.format(z.volume),
                'z-score:buy-ratio > thresh':
                '{:+.2f} > 0.5'.format(z.buy_ratio)
            }
        }
    elif record:
        if ema.iloc[-1] >= max(
            [x['ema_pct_change'] for x in record['snapshots']]):
            return {'action': None, 'snapshot': snapshot}

        return {
            'action': 'sell',
            'snapshot': {
                **snapshot,
                **{
                    'bid_price': float(ob['bidPrice'])
                }
            },
            'details': {
                'ema:slope <= thresh':
                '{:+.2f}% <= 0'.format(ema.iloc[-1]),
                'AND bid < buy':
                '{:} < {:.8f}'.format(ob['bidPrice'],
                                      record['snapshots'][0]['ask_price'])
            }
        }
コード例 #21
0
ファイル: strategy.py プロジェクト: HAMMERjah/coincruncher
def _zscore(candle, record=None):
    """Evaluate z-score vs buy/sell thresholds.
    Buy: Z-Score below threshold
    Sell: Z-Score returning to mean
    """
    if candle['freq'] != '1m':
        return None

    periods = rules['z-score']['periods']
    z = signals.z_score(candle, periods)
    ema = signals.ema_pct_change(candle, rules['ema']['span'])

    snapshot = {
        'time': now(),
        'price': candle['close'],
        'volume': candle['volume'],
        'buy_ratio': candle['buy_ratio'],
        'z-score': z.to_dict(),
        'ema_pct_change': ema.iloc[-1]
    }

    if record is None:
        threshold = rules['z-score']['buy_thresh']
        if z.close > threshold:
            print('z.close < threshold')
            return None

        client = Client("", "")
        ob = client.get_orderbook_ticker(symbol=candle['pair'])

        return {
            'strategy': 'z-score',
            'snapshot': {
                **snapshot,
                **{
                    'ask_price': float(ob['askPrice'])
                }
            },
            'details': {
                'close:z-score < thresh':
                '{:+.2f} < {:+.2f}'.format(z.close, threshold)
            }
        }
    elif record:
        threshold = rules['z-score']['sell_thresh']
        if z.close < threshold:
            return {'action': None, 'snapshot': snapshot}

        if ema.iloc[-1] > 0.10:
            return {'action': None, 'snapshot': snapshot}

        client = Client("", "")
        ob = client.get_orderbook_ticker(symbol=candle['pair'])

        return {
            'action': 'sell',
            'snapshot': {
                **snapshot,
                **{
                    'bid_price': float(ob['bidPrice'])
                }
            },
            'details': {
                'close:z-score > thresh': '{:+.2f} > -0.75'.format(z.close),
                'ema:slope < thresh': '{:+.2f}% <= 0.10'.format(ema.iloc[-1])
            }
        }