BitShares Forum
Main => General Discussion => Topic started by: JianJolly on April 04, 2017, 08:16:24 pm
-
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 (http://docs.pybitshares.com/en/latest/), 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.
#!/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.
#!/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.
-
Try this:
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
-
Try this:
[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
-
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!
If so, I'd go with the more primitive file writing-reading version.
Totally up to you, of course :)
-
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.