Compare commits

...

4 Commits

Author SHA1 Message Date
becffcd1fb implement SMA, EMA in addition to WMA 2022-07-13 13:22:57 +09:00
3d2a454e96 fix a code to terminate process 2022-07-13 11:17:07 +09:00
a5f951dea9 improve WMA algorithm 2022-07-13 08:27:16 +09:00
ecb9ad08e1 add WMA algorithm with bitbank bot 2022-07-12 23:03:32 +09:00
8 changed files with 367 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
__pycache__/
secret.json

2
algorithm/__init__.py Normal file
View File

@@ -0,0 +1,2 @@
from algorithm.ma import SMA, WMA, EMA, MA_Cross
from algorithm.status import Status

64
algorithm/ma.py Normal file
View File

@@ -0,0 +1,64 @@
class SMA:
def __init__(self):
return
def calc(self, cands, period):
sum = 0
n = min(period, len(cands))
for i in range(n):
sum += cands[i][3]
return sum / n
class EMA:
def __init__(self):
return
def calc(self, cands, period):
n = min(period, len(cands))
ret = 0
for i in range(n):
ret += cands[i][3]
ret /= n
for i in range(n):
ret = (2/(1+n))*(cands[n-i-1][3] - ret) + ret
return ret
class WMA:
def __init__(self):
return
def calc(self, cands, period):
sum = 0
den = 0
for i in range(min(period, len(cands))):
sum += cands[i][3] * (i+1)
den += i+1
return sum / den
class MA_Cross:
def __init__(self, ma, unit, width):
self._ma = ma
self._unit = unit
self._width = width
self._period_short = 5
self._period_mid = 21
self._period_long = 55
self._posi_amp = 0.1
def judge(self, status):
cands = status.candles[self._unit]
short = self._ma.calc(cands, self._period_short)
mid = self._ma.calc(cands, self._period_mid)
long = self._ma.calc(cands, self._period_long)
width = mid * self._width
posi = max(-1, min((short-long)/(2.5*width), 1)) * self._posi_amp
diff = short - mid
if diff < 0:
return max(-1, diff/width+posi) # sell
else:
return min(1, diff/width+posi) # buy

4
algorithm/status.py Normal file
View File

@@ -0,0 +1,4 @@
class Status:
def __init__(self):
self.price = 0
self.candles = {"1m": [], "1h": []}

163
bitbank.py Normal file
View File

@@ -0,0 +1,163 @@
import datetime
import json
import python_bitbankcc
import random
import sys
import time
import algorithm as algo
FETCH_INTERVAL = 5
status = {}
def init():
try:
sec = json.load(open("secret.json"))
sec = sec["bitbank"]
pub = python_bitbankcc.public()
pri = python_bitbankcc.private(sec["key"], sec["secret"])
except Exception as e:
print("failed to initialize bitbank features: ", e)
return []
procs = []
procs.append(Fetcher(pub, "xlm_jpy", 10*60))
procs.append(Proc(pri, "xlm_jpy", 5000, algo.MA_Cross(algo.EMA(), "1h", 0.01), 10*60))
procs.append(Fetcher(pub, "btc_jpy", 10*60))
procs.append(Proc(pri, "btc_jpy", 5000, algo.MA_Cross(algo.EMA(), "1h", 0.01), 10*60))
procs.append(Fetcher(pub, "eth_jpy", 10*60))
procs.append(Proc(pri, "eth_jpy", 5000, algo.MA_Cross(algo.EMA(), "1h", 0.01), 10*60))
procs.append(Fetcher(pub, "matic_jpy", 5))
procs.append(Proc(pri, "matic_jpy", 1000, algo.MA_Cross(algo.EMA(), "1m", 0.01), 5))
return procs
class Fetcher:
def __init__(self, pub, pair, interval):
self._interval = interval
self._last_update = 0
self._pub = pub
self._pair = pair
self._candle_prev = {}
def tick(self):
now = time.time()
if now-self._last_update < self._interval:
return
try:
st = algo.Status()
st.candles["1m"] = self._get_candle("1min", 60)
st.candles["1h"] = self._get_candle("1hour", 60)
st.price = st.candles["1m"][0][3]
global status
status[self._pair] = st
except Exception as e:
print("fetce error:", e)
self._last_update = now
def _get_candle(self, unit, n, t = datetime.datetime.now(tz=datetime.timezone.utc)):
data = self._pub.get_candlestick(
self._pair,
unit,
t.strftime("%Y%m%d"))
data = data["candlestick"][0]["ohlcv"]
ret = []
for i in range(min(n, len(data))):
j = len(data)-i-1
ret.append([float(data[j][0]), float(data[j][1]), float(data[j][2]), float(data[j][3])])
if len(ret) < n:
t = t - datetime.timedelta(days=1)
data = self._get_candle(unit, n-len(ret), t)
ret = [*ret, *data]
return ret
class Proc:
def __init__(self, pri, pair, asset, algo, interval):
self._pri = pri
self._pair = pair
self._last_update = 0
self._now = 0
self._bsi = 0
self._algo = algo
self._interval = interval
self._last_order = None
self._buy_price = 0
self._asset = asset
def tick(self):
self._now = time.time()
if self._now-self._last_update > self._interval:
self._update()
self._last_update = self._now
def _update(self):
global status
st = status[self._pair]
pbsi = self._bsi
bsi = self._algo.judge(status[self._pair])
if bsi != 0:
self._bsi = bsi
print(f"[bitbank: {self._pair}] BSI {pbsi} -> {bsi}")
if bsi*pbsi < 0:
if bsi > 0:
try:
amount = self._asset/st.price
self._order_market(amount, "buy")
self._buy_price = st.price
print(f"[bitbank: {self._pair}] <BUY> +{amount} / -{self._asset}")
except Exception as e:
print(f"[bitbank: {self._pair}] buy error", e)
self._buy_price = 0
elif self._buy_price > 0:
try:
amount = self._asset/self._buy_price
self._order_market(amount, "sell")
print(f"[bitbank: {self._pair}] <SELL> -{amount} / +{amount*st.price}")
except Exception as e:
print(f"[bitbank: {self._pair}] sell error", e)
def _order_market(self, amount, sell_or_buy):
order = self._pri.order(self._pair, None, str(amount), sell_or_buy, "market")
self._last_order = order
def _order_limit(self, price, sell_or_buy):
try_price = price
for i in range(10):
amount = self._asset/price
order = self._pri.order(self._pair, str(price), str(amount), sell_or_buy, "limit", True)
time.sleep(.1)
order = self._pri.get_order(self._pair, order["order_id"])
if order["status"] == "UNFILLED":
return
move = random.random()*10+1
if sell_or_buy == "sell":
price = price-move
else:
price = price+move
if sell_or_buy == "sell":
self._pri.order(self._pair, None, str(amount), sell_or_buy, "market")
print(f"[bitbank: {self._pair}] <MARKET-SELL> amount: {amount}")
else:
raise Exception("tried 10 times.... X(")

124
main.py Normal file
View File

@@ -0,0 +1,124 @@
import multiprocessing
import os
import signal
import time
import bitbank
alive = True
def main():
global alive
procs = [*bitbank.init()]
while alive:
for i in range(len(procs)):
procs[i].tick()
time.sleep(0.5)
def on_exit(sig, x):
global alive
alive = False
proc = multiprocessing.Process(target = main)
proc.start()
signal.signal(signal.SIGTERM, on_exit)
signal.pause()
proc.join()
# while True:
# print("start tick")
#
# # ---- get assets
# assets = pri.get_asset()["assets"]
# onhand = {}
# locked = {}
# for i in range(len(assets)):
# coin = assets[i]["asset"]
# onhand[coin] = float(assets[i]["onhand_amount"])
# locked[coin] = float(assets[i]["locked_amount"])
#
# # ---- get current price
# candles = pub.get_candlestick("btc_jpy", "1min", now.strftime("%Y%m%d"))["candlestick"][0]["ohlcv"];
# latest_candle = candles[len(candles)-1]
# begin = int(latest_candle[0])
# high = int(latest_candle[1])
# low = int(latest_candle[2])
# end = int(latest_candle[3])
#
# # ---- cancel existing orderes
# prev_orders = pri.get_active_orders("btc_jpy")["orders"]
# cancel_orders = []
# for i in range(len(prev_orders)):
# id = prev_orders[i]["order_id"]
# status = prev_orders[i]["status"]
# side = prev_orders[i]["side"]
# price = int(prev_orders[i]["price"])
#
# if status == "UNFILLED":
# diff = abs(end-price)/end
# if side == "buy":
# if diff < -0.0005:
# cancel_orders.append(str(id))
# elif side == "sell":
# if diff > 0.003:
# cancel_orders.append(str(id))
#
# if len(cancel_orders) > 0:
# pri.cancel_orders("btc_jpy", cancel_orders)
# print("cancelled", len(cancel_orders), "orders")
# if len(prev_orders)-len(cancel_orders) > 8:
# print("skip making an order because there are too many orderes")
# time.sleep(30)
# continue
#
# # ---- get transactions
# trans = pub.get_transactions("btc_jpy")["transactions"]
# buy_min = low
# sell_max = high
# for i in range(len(trans)):
# exec_at = datetime.datetime.fromtimestamp(trans[i]["executed_at"]/1000)
# side = trans[i]["side"]
# price = int(trans[i]["price"])
# if now-exec_at < datetime.timedelta(minutes=1):
# if side == "buy":
# buy_min = min(buy_min, price)
# elif side == "sell":
# sell_max = max(sell_max, price)
#
# # ---- make an order
# buy_coe = 0.5
# ben_coe = 0.8
#
# diff = (end-begin)/end
# if diff > 0.002:
# buy_coe -= clamp(100*diff/end, 0, 1)*0.2
# elif diff < -0.002:
# buy_coe += clamp(100*diff, 0, 1)*0.2
#
# print("buy-min:", buy_min, "| sell-max:", sell_max, "| diff:", sell_max-buy_min)
# diff = sell_max-buy_min
# avg = (sell_max+buy_min)/2
#
# buy_price = avg - diff*buy_coe*ben_coe
# sell_price = avg + diff*(1-buy_coe)*ben_coe
#
# buy_jpy = max((onhand["jpy"]-locked["jpy"])*0.1, (onhand["btc"]-locked["btc"])*end*0.1)
# buy_btc = buy_jpy/buy_price
# sell_jpy = buy_btc*sell_price
# sell_btc = buy_btc
#
# try:
# pri.order("btc_jpy", str(buy_price), str(buy_btc), "buy", "limit", True)
# print("[buy] btc:", buy_btc, "| jpy:", -buy_jpy, "| price:", buy_price)
# except Exception as e:
# print(e)
#
# try:
# pri.order("btc_jpy", str(sell_price), str(sell_btc), "sell", "limit", True)
# print("[sel] btc:", -sell_btc, "| jpy:", sell_jpy, "| price:", sell_price)
# except Exception as e:
# print(e)
#
# time.sleep(10)

2
requirements.txt Normal file
View File

@@ -0,0 +1,2 @@
git+https://github.com/bitbankinc/python-bitbankcc@3aee8a6ef9d4616e11f044c2a1574ec389671675#egg=python-bitbankcc
requests

6
secret.json.default Normal file
View File

@@ -0,0 +1,6 @@
{
"bitbank": {
"key": "your key",
"secret": "your secret"
}
}