Author Topic: Jolly Scripts  (Read 2493 times)

0 Members and 1 Guest are viewing this topic.

Offline JianJolly

  • Newbie
  • *
  • Posts: 14
    • View Profile
I added a simple market making script.

This one fetches price data from an outer source (this source is Bitstamp below) and gives market making orders in the DEX. Bitstamp's "last price" in the BTC/USD market is converted to TRY via python-forex, and this data is checked once a minute. The script gives bid orders at the prices which are 1%, 1.5% and 2% lower than the "last price", and records the price as Bot.ref_price, i.e., the reference price. Three corresponding ask orders are also given at the prices which are 1%, 1.5% and 2% above the reference price. If the price source changes more than 0.5% according to the reference price, the orders are canceled and a new set of bids and asks are given, changing the reference price.
« Last Edit: May 01, 2017, 12:15:33 am by JianJolly »

Offline xeroc

  • Board Moderator
  • Hero Member
  • *****
  • Posts: 12922
  • ChainSquad GmbH
    • View Profile
    • ChainSquad GmbH
  • BitShares: xeroc
  • GitHub: xeroc
There is one question, though. When we construct the webpage with Flask's route, does the script send a request to the API node each time someone browses the page?
YES, although you can add caching if you need it!

Quote
If so, I'd go with the more primitive file writing-reading version.
Totally up to you, of course :)

Offline JianJolly

  • Newbie
  • *
  • Posts: 14
    • View Profile
Try this:

Code: [Select]
[code block omitted from the quote for a clearer view]

Remark: The "difficult" looking stuff in the return of index() is only to "stringify" each of the keys in the market ticker dictionary.
This makes the output look 'nice'. If you where to return 'ticker' directly with  "return jsonify(ticker)" .. you would get a whole lot more information that is unfortunately, way less useful for most people :D

Thanks for the insight! Yes, I tried json.dumps(market.ticker()) and it gave a lot more information. In my example, I manually do all the work which is done by the two Flask modules in your version. There is one question, though. When we construct the webpage with Flask's route, does the script send a request to the API node each time someone browses the page? If so, I'd go with the more primitive file writing-reading version.

Best,

Jian
« Last Edit: April 05, 2017, 02:28:34 pm by JianJolly »

Offline xeroc

  • Board Moderator
  • Hero Member
  • *****
  • Posts: 12922
  • ChainSquad GmbH
    • View Profile
    • ChainSquad GmbH
  • BitShares: xeroc
  • GitHub: xeroc
Try this:

Code: [Select]
from flask import Flask, jsonify
from bitshares.market import Market


app = Flask(__name__)
market = Market("USD:BTS")


@app.route('/')
def index():
    ticker = market.ticker()
    return jsonify({k: str(v) for k, v in ticker.items()})


if __name__ == "__main__":
    app.run(debug=True)

Remark: The "difficult" looking stuff in the return of index() is only to "stringify" each of the keys in the market ticker dictionary.
This makes the output look 'nice'. If you where to return 'ticker' directly with  "return jsonify(ticker)" .. you would get a whole lot more information that is unfortunately, way less useful for most people :D

Offline JianJolly

  • Newbie
  • *
  • Posts: 14
    • View Profile
Hi Everyone,

Here I will put some of the things I am playing with, and hope that it will go in a meaningful direction as I have more goodies to put together.

* ticker *

The first one is a ticker for the markets in the BTS DEX. It depends on pybitshares, and is meant to run as a daemon which has the permission to write to a file on a web server. It chekcs the ticker and waits for 60 seconds, and repeats this forever. The web server will be public for everyone to request ticker instead of requesting it from the API directly, thereby reducing the load.

A concern that may come out of this would be about security. Let's say a bot uses this ticker to yield trading signals. Thus, one of the things on which the reliability of the bot depends is the quality of the ticker. Concerning that, what preventive measures could the server running the script take in order to minimize the relevant risks? That is the question I am now working on.


Code: [Select]
#!/usr/bin/env python3


from time import sleep
from json import dumps
from math import floor

from bitshares import BitShares
from bitshares.instance import set_shared_bitshares_instance
from bitshares.market import Market
from bitshares.blockchain import Blockchain


def value(string, decimals):
    num_string = str.split(str(string))[0]
    no_comma = ""
    for char in num_string:
      if (char != ','):
        no_comma += char
    return round_down(float(no_comma), decimals)

def round_down(number, decimals):
    return floor(number * pow(10, decimals)) / pow(10, decimals)

def ticker(market_string, decimals=[8,8]):
    """ e.g. market_string = "OPEN.BTC:KAPITAL"
        e.g. decimals = [8, 5] """
    try:

        (quote, base) = market_string.split(":")
        if (base == "KAPITAL"):
            decimals[1] = 5
        bitshares = BitShares("wss://eu.openledger.info/ws")
        # alternative: wss://secure.freedomledger.com/ws
        # wss://eu.openledger.info/ws
        set_shared_bitshares_instance(bitshares)
        chain = Blockchain()
        block_num = chain.get_current_block_num()
        market = Market(market_string)
        ticker = market.ticker()
        last = value(ticker["latest"], decimals[1])
        try:
            avg = round_down(value(ticker["baseVolume"], decimals[1]) / \
            value(ticker["quoteVolume"], decimals[0]), decimals[1])
        except:
            avg = last
        change = format(ticker["percentChange"] / 100, '.5f')
        # e.g. for 1.2% change, change = 1.2

        response = {
            "status": "success",
            "data": {
                "market": market_string,
                # max and min values are to be added
                # "max": max_value,
                # "min": min_value,
                "avg": avg,
                "vol": value(ticker["quoteVolume"], decimals[0]),
                "bid": value(ticker["highestBid"], decimals[1]),
                "ask": value(ticker["lowestAsk"], decimals[1]),
                "last": last,
                "change": change
            },
            "message": "Block: " + str(block_num)
        }
        return response
    except Exception as e:
        return {"status": "error", "data": {}, "message": e}


if __name__ == "__main__":
    while True:
        market_dict = {"btc": "OPEN.BTC:KAPITAL", "eth": "OPEN.ETH:KAPITAL",
                       "bts": "BTS:KAPITAL", "dash": "OPEN.DASH:KAPITAL",
                       "usd": "OPEN.USDT:KAPITAL", "doge": "OPEN.DOGE:KAPITAL",
                       "maid": "OPEN.MAID:KAPITAL", "ltc": "OPEN.LTC:KAPITAL" }
        for k in market_dict:
            response = ticker(market_dict[k])
            print(response)
            if (response["status"] == "success"):
                # with open("/var/www/html/ticker/" + k + ".json", "w") as f:
                # with open(k + ".json", "w") as f:
                with open(k + ".json", "w") as f:
                    response_text = dumps(response,
                                          indent=4,
                                          separators=(',', ': '))
                    f.write(response_text)
            else:
                print(response["message"])
            sleep(6)

The outputs may be seen here: https://bitkapital.com/ticker/


* market-maker *

This one fetches price data from an outer source (this source is Bitstamp below) and gives market making orders in the DEX. Bitstamp's "last price" in the BTC/USD market is converted to TRY via python-forex, and this data is checked once a minute. The script gives bid orders at the prices which are 1%, 1.5% and 2% lower than the "last price", and records the price as Bot.ref_price, i.e., the reference price. Three corresponding ask orders are also given at the prices which are 1%, 1.5% and 2% above the reference price. If the price source changes more than 0.5% according to the reference price, the orders are canceled and a new set of bids and asks are given, changing the reference price.


Code: [Select]
#!/usr/bin/env python3


import json
from math import floor
from time import sleep
from datetime import datetime
from datetime import timedelta

# from the package forex-python
from forex_python.converter import CurrencyRates

import bitfinex
from poloniex.poloniex import Poloniex
from bitstamp import bitstamp

from bitshares.bitshares.bitshares import BitShares
from bitshares.bitshares.instance import set_shared_bitshares_instance
from bitshares.bitshares.market import Market
from bitshares.bitshares.account import Account


TIME_ZONE = 3
LOG_PATH = "/var/www/html/"
LOG_FILENAME = "index.html"


def round_down(number, decimals):
    return floor(number * pow(10, decimals)) / pow(10, decimals)

def append_log(msg, path=LOG_PATH, filename=LOG_FILENAME, timestamp=True):
    try:
        with open(path + filename, "r") as f:
            contents = f.read()
    except:
        contents = ""

    with open(path + filename, "w") as f:
        if timestamp:
            today = datetime.utcnow() + timedelta(hours=TIME_ZONE)
            if today.month is 1:
                month = "Ocak"
            elif today.month is 2:
                month = "Subat"
            elif today.month is 3:
                month = "Mart"
            elif today.month is 4:
                month = "Nisan"
            elif today.month is 5:
                month = "Mayis"
            elif today.month is 6:
                month = "Haziran"
            elif today.month is 7:
                month = "Temmuz"
            elif today.month is 8:
                month = "Agustos"
            elif today.month is 9:
                month = "Eylul"
            elif today.month is 10:
                month = "Ekim"
            elif today.month is 11:
                month = "Kasim"
            elif today.month is 12:
                month = "Aralik"
            if today.hour < 10:
                hour = "0" + str(today.hour)
            else:
                hour = str(today.hour)
            if today.minute < 10:
                minute = "0" + str(today.minute)
            else:
                minute = str(today.minute)

            contents = str(today.day) + ' ' + month + ' ' + str(today.year) + \
                " " + hour + ":" + minute + " ||| " + msg + "<br/>\n" + contents
        else:
            contents = msg + "<br/>\n" + contents

        f.write(contents)

class Price(object):
    def __init__(self, ref_price):
        self.forex_USDTRY = CurrencyRates().get_rates("USD")["TRY"]

        self.polo_ticker = Poloniex("", "").returnTicker()["USDT_BTC"]
        self.polo_BTCTRY = round(float(self.polo_ticker["last"]) * \
                                 self.forex_USDTRY, 5)

        self.finex_ticker = bitfinex.publicAPI.Ticker("BTCUSD").response
        self.finex_BTCTRY = round(float(json.loads(self.finex_ticker)
                                  ["last_price"]) * self.forex_USDTRY, 5)

        self.stamp_ticker = bitstamp.client.Public().ticker()
        self.stamp_BTCTRY = round(float(self.stamp_ticker["last"]) * \
                                  self.forex_USDTRY, 5)

        if ref_price is "":
            self.ref_price = {"source": ref_price, "value": -1}
        elif ref_price is "polo_BTCTRY":
            self.ref_price = {"source": ref_price, "value": self.polo_BTCTRY}
        elif ref_price is "finex_BTCTRY":
            self.ref_price = {"source": ref_price, "value": self.finex_BTCTRY}
        elif ref_price is "stamp_BTCTRY":
            self.ref_price = {"source": ref_price, "value": self.stamp_BTCTRY}

    def update(self):
        self.__init__(self.ref_price["source"])
        return self

class Bot(object):
    def __init__(self, wss, WIF, price_source):
        # initialize bitshares instance, account, market, and reference price
        self.bitshares = BitShares(wss, keys=[WIF], expiration=6000)
        set_shared_bitshares_instance(self.bitshares)
        self.account = Account("market-buster", full=True)
        self.market = Market("OPEN.BTC:KAPITAL")
        self.price_obj = Price(price_source)

        # strategy params
        # minimum change in the price source to trigger trade
        self.trigger_threshold = 0.005
        # waiting period between each market check in minutes
        self.waiting_period = 1
        # profit margins for the order grid
        self.profit_margins_normal = [0.01, 0.015, 0.02]
        self.profit_margins_tight = [0.004, 0.005, 0.006]

    def open_orders(self):
        return Account("market-buster", full=True).openorders

    def cancel_all(self):
        open_orders = [order["id"] for order in self.open_orders()]
        if len(open_orders)> 0:
            self.market.cancel(open_orders, account="market-buster")
            append_log("Tum emirler iptal edildi.")

    def balances(self, log=False):
        """
        make sure self.ref_price is up-to-date
        when calling this method with log=True
        """
        try: KAPITAL = self.account.balance("KAPITAL").amount
        except: KAPITAL = 0
        try: BTC = self.account.balance("OPEN.BTC").amount
        except: BTC = 0

        if log:
            append_log("Bakiye: " + str(KAPITAL) + " KAPITAL, " + str(BTC) + \
                       " OPEN.BTC. Guncel hesap degeri: " + \
                       str(round_down(KAPITAL + BTC * self.ref_price, 2)) + \
                       " TL")

        return {"KAPITAL": KAPITAL, "BTC": BTC}

    def run(self):
        self.ref_price = self.price_obj.update().ref_price["value"]
        append_log("Referans fiyat: " + str(self.ref_price))
        while True:
            self.cancel_all()
            self.balance = self.balances(log=True)

            # strategy rule 1: profit margins
            KAPITAL_ratio = self.balance["KAPITAL"] / (self.balance["KAPITAL"] \
                            + self.balance["BTC"] * self.ref_price)
            if (KAPITAL_ratio < 0.125):
                profit_margins_bid = self.profit_margins_normal
                profit_margins_ask = self.profit_margins_tight
                append_log("Dusuk KAPITAL bakiyesi. Satis marji dusuruldu.")
            if (KAPITAL_ratio > 0.875):
                profit_margins_bid = self.profit_margins_tight
                profit_margins_ask = self.profit_margins_normal
                append_log("Dusuk BTC bakiyesi. Alis marji dusuruldu.")
            # end strategy

            # strategy rule 2: bid
            balance_per_bid = round_down(self.balance["KAPITAL"] / \
                len(profit_margins_bid), 5)
            for i, profit_margin in enumerate(profit_margins_bid):
                price = round_down(self.ref_price * (1 - profit_margin), 5)
                amount = round_down(balance_per_bid / price, 8)
                if amount > 0:
                    self.market.buy(price, amount, account="market-buster")
                    append_log("BID @ " + str(price))
                elif i is 0:
                    append_log("YETERSIZ BAKIYE! KAPITAL yukleyin.")
            # end strategy

            # strategy rule 3: ask
            balance_per_ask = round_down(self.balance["BTC"] / \
                len(profit_margins_ask), 8)
            for i, profit_margin in enumerate(profit_margins_ask):
                price = round_down(self.ref_price * (1 + profit_margin), 5)
                amount = balance_per_ask
                if amount > 0:
                    self.market.sell(price, amount, account="market-buster")
                    append_log("ASK @ " + str(price))
                elif i is 0:
                    append_log("YETERSİZ BAKİYE! BTC yükleyin.")
            # end strategy

            sleep(self.waiting_period * 60)

            while True:
                # strategy rule 4: price update
                new_price = self.price_obj.update().ref_price["value"]
                upper_threshold = self.ref_price * (1 + self.trigger_threshold)
                lower_threshold = self.ref_price * (1 - self.trigger_threshold)

                if new_price >= upper_threshold or new_price <= lower_threshold:
                    append_log("Referans fiyat: " + str(self.ref_price) + \
                               " --> " + str(new_price))
                    self.ref_price = new_price
                    break
                # end strategy

                # strategy rule 5: filled orders
                no_bid = True
                no_ask = True
                orders = self.open_orders()
                for order in orders:
                    if order["base"]["asset"]["symbol"] == "KAPITAL" and \
                       order["quote"]["asset"]["symbol"] == "OPEN.BTC":
                        no_bid = False
                    if order["base"]["asset"]["symbol"] == "OPEN.BTC" and \
                       order["quote"]["asset"]["symbol"] == "KAPITAL":
                        no_ask = False

                should_break = False
                self.balance = self.balances()
                if no_bid:
                    if round_down(round_down(self.balance["KAPITAL"] / \
                       len(self.profit_margins), 5) / new_price, 8) > 0:
                        self.ref_price = new_price
                        should_break = True
                if no_ask:
                    if round_down(self.balance["BTC"] / \
                       len(self.profit_margins), 5) > 0:
                        self.ref_price = new_price
                        should_break = True
                if should_break:
                    break
                # end strategy

                sleep(self.waiting_period * 60)


if __name__ == "__main__":
    wss = "wss://eu.openledger.info/ws"
    WIF = "<WIF>"
    price_source = "stamp_BTCTRY"
    bot = Bot(wss, WIF, price_source)
    bot.run()

You may just disregard the append_log function, which has some Turkish in it for logging purposes.
« Last Edit: May 02, 2017, 10:47:59 pm by JianJolly »