Compare commits
No commits in common. "f6413a338996c660acd0f2d9ee4942b46af8566f" and "8f34deb5589131b8c8a3a00fabd368bc91d131a9" have entirely different histories.
f6413a3389
...
8f34deb558
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,2 +0,0 @@
|
|||||||
__pycache__/
|
|
||||||
/config.json
|
|
70
algo/mm.py
70
algo/mm.py
@ -1,70 +0,0 @@
|
|||||||
# No copyright
|
|
||||||
import asyncio
|
|
||||||
import logging
|
|
||||||
|
|
||||||
class MM:
|
|
||||||
def __init__(self, player, pair, config):
|
|
||||||
self._player = player
|
|
||||||
self._pair = pair
|
|
||||||
|
|
||||||
self._lastBuy = ""
|
|
||||||
self._lastSell = ""
|
|
||||||
|
|
||||||
self._delta = float(config["delta"])
|
|
||||||
|
|
||||||
async def update(self):
|
|
||||||
lastBuyAgreed = (self._lastBuy is not None and
|
|
||||||
self._lastBuy not in self._player.orders)
|
|
||||||
lastSellAgreed = (self._lastSell is not None and
|
|
||||||
self._lastSell not in self._player.orders)
|
|
||||||
|
|
||||||
if not lastBuyAgreed and not lastSellAgreed:
|
|
||||||
return
|
|
||||||
|
|
||||||
cancel_orders = []
|
|
||||||
if not lastBuyAgreed and self._lastBuy is not None:
|
|
||||||
cancel_orders.append(self._lastBuy)
|
|
||||||
self._lastBuy = None
|
|
||||||
if not lastSellAgreed and self._lastSell is not None:
|
|
||||||
cancel_orders.append(self._lastSell)
|
|
||||||
self._lastSell = None
|
|
||||||
if len(cancel_orders) != 0:
|
|
||||||
await self._player.cancel(self._pair.names, cancel_orders)
|
|
||||||
|
|
||||||
sales = self._player.assets[self._pair.names[0]]
|
|
||||||
crncy = self._player.assets[self._pair.names[1]]
|
|
||||||
price = self._pair.ticker.price
|
|
||||||
|
|
||||||
lot = (sales.amount + crncy.amount / price) / 4
|
|
||||||
pos = sales.amount / lot - 2
|
|
||||||
|
|
||||||
buy_amount = _quad( pos) * lot
|
|
||||||
sell_amount = _quad(-pos) * lot
|
|
||||||
|
|
||||||
buy_price = price * (1 - self._delta);
|
|
||||||
sell_price = price * (1 + self._delta);
|
|
||||||
|
|
||||||
order_unit = pow(10, -sales.precision)
|
|
||||||
|
|
||||||
async def buy():
|
|
||||||
return (None if order_unit > buy_amount else
|
|
||||||
await self._player.orderLimitBuy(
|
|
||||||
self._pair.names, amount = buy_amount, price = buy_price))
|
|
||||||
|
|
||||||
async def sell():
|
|
||||||
return (None if order_unit > sell_amount else
|
|
||||||
await self._player.orderLimitSell(
|
|
||||||
self._pair.names, amount = sell_amount, price = sell_price))
|
|
||||||
|
|
||||||
orders = await asyncio.gather(buy(), sell())
|
|
||||||
self._lastBuy = orders[0]
|
|
||||||
self._lastSell = orders[1]
|
|
||||||
|
|
||||||
# https://kijitora-2018.hatenablog.com/entry/2018/12/23/102913
|
|
||||||
def _quad(x):
|
|
||||||
if x < -1:
|
|
||||||
return 1
|
|
||||||
if x <= 1:
|
|
||||||
return -1/4 * (x+1)**2 + 1
|
|
||||||
return 0
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
|||||||
version: '3'
|
|
||||||
|
|
||||||
services:
|
|
||||||
env:
|
|
||||||
image: python:3-alpine
|
|
||||||
command: sh -c "pip3 install -q -r requirements.txt && python3 main.py"
|
|
||||||
working_dir: /repo/
|
|
||||||
volumes:
|
|
||||||
- ./:/repo/
|
|
50
main.py
50
main.py
@ -1,50 +0,0 @@
|
|||||||
# No copyright
|
|
||||||
|
|
||||||
import asyncio
|
|
||||||
import json
|
|
||||||
import logging
|
|
||||||
import pybotters
|
|
||||||
import signal
|
|
||||||
|
|
||||||
from util.pair import Pair
|
|
||||||
from util.player import Player
|
|
||||||
from algo.mm import MM
|
|
||||||
|
|
||||||
with open("config.json", "r") as file:
|
|
||||||
config = json.load(file)
|
|
||||||
|
|
||||||
async def main():
|
|
||||||
alive = True
|
|
||||||
logging.basicConfig(level=logging.DEBUG)
|
|
||||||
|
|
||||||
def teardown():
|
|
||||||
nonlocal alive
|
|
||||||
alive = False
|
|
||||||
for t in asyncio.all_tasks():
|
|
||||||
t.cancel()
|
|
||||||
|
|
||||||
loop = asyncio.get_event_loop()
|
|
||||||
loop.add_signal_handler(signal.SIGTERM, teardown)
|
|
||||||
loop.add_signal_handler(signal.SIGINT, teardown)
|
|
||||||
|
|
||||||
async with pybotters.Client(apis={"bitbank":config["auth"]}) as pb:
|
|
||||||
player = Player(pb)
|
|
||||||
pair = Pair(pb, config["pair"])
|
|
||||||
algo = MM(player, pair, config["algorithm"]["mm"])
|
|
||||||
while alive:
|
|
||||||
try:
|
|
||||||
await asyncio.gather(
|
|
||||||
player.update(),
|
|
||||||
pair.update())
|
|
||||||
await algo.update()
|
|
||||||
except Exception as e:
|
|
||||||
print(e)
|
|
||||||
logging.error(e)
|
|
||||||
await asyncio.sleep(int(config["interval"]))
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
try:
|
|
||||||
loop = asyncio.new_event_loop()
|
|
||||||
loop.run_until_complete(main())
|
|
||||||
finally:
|
|
||||||
loop.close()
|
|
@ -1 +0,0 @@
|
|||||||
pybotters
|
|
23
util/pair.py
23
util/pair.py
@ -1,23 +0,0 @@
|
|||||||
# No copyright
|
|
||||||
import logging
|
|
||||||
|
|
||||||
class Pair:
|
|
||||||
def __init__(self, pb, names):
|
|
||||||
self.names = names
|
|
||||||
self._pb = pb
|
|
||||||
|
|
||||||
async def update(self):
|
|
||||||
ticker = (await self._get("ticker"))["data"]
|
|
||||||
self.ticker = Ticker(ticker)
|
|
||||||
|
|
||||||
async def _get(self, suffix):
|
|
||||||
res = await self._pb.get(f"https://public.bitbank.cc/{self.names[0]}_{self.names[1]}/{suffix}")
|
|
||||||
json = await res.json()
|
|
||||||
if "success" not in json or 1 != json["success"]:
|
|
||||||
code = json["data"]["code"]
|
|
||||||
raise Exception(f"API error: {code}")
|
|
||||||
return json
|
|
||||||
|
|
||||||
class Ticker:
|
|
||||||
def __init__(self, json):
|
|
||||||
self.price = float(json["last"])
|
|
@ -1,77 +0,0 @@
|
|||||||
# No copyright
|
|
||||||
import logging
|
|
||||||
|
|
||||||
class Player:
|
|
||||||
def __init__(self, pb):
|
|
||||||
self._pb = pb
|
|
||||||
self.assets = {}
|
|
||||||
self.orders = {}
|
|
||||||
|
|
||||||
async def cancel(self, pair, orders):
|
|
||||||
res = await self._post("user/spot/cancel_orders", data={
|
|
||||||
"pair": f"{pair[0]}_{pair[1]}",
|
|
||||||
"order_ids": orders
|
|
||||||
})
|
|
||||||
self._check(res)
|
|
||||||
|
|
||||||
async def orderLimitSell(self, pair, amount, price):
|
|
||||||
res = await self._post("user/spot/order", data={
|
|
||||||
"pair": f"{pair[0]}_{pair[1]}",
|
|
||||||
"amount": str(amount),
|
|
||||||
"price": str(price),
|
|
||||||
"side": "sell",
|
|
||||||
"type": "limit",
|
|
||||||
})
|
|
||||||
return self._check(res)["data"]["order_id"]
|
|
||||||
|
|
||||||
async def orderLimitBuy(self, pair, amount, price):
|
|
||||||
res = await self._post("user/spot/order", data={
|
|
||||||
"pair": f"{pair[0]}_{pair[1]}",
|
|
||||||
"amount": str(amount),
|
|
||||||
"price": str(price),
|
|
||||||
"side": "buy",
|
|
||||||
"type": "limit",
|
|
||||||
})
|
|
||||||
return self._check(res)["data"]["order_id"]
|
|
||||||
|
|
||||||
async def update(self):
|
|
||||||
self.assets = {}
|
|
||||||
self.orders = {}
|
|
||||||
|
|
||||||
assets = (await self._get("user/assets"))["data"]["assets"]
|
|
||||||
for asset in assets:
|
|
||||||
self.assets[asset["asset"]] = Asset(asset)
|
|
||||||
|
|
||||||
orders = (await self._get("user/spot/active_orders"))["data"]["orders"]
|
|
||||||
for order in orders:
|
|
||||||
self.orders[order["order_id"]] = Order(order)
|
|
||||||
|
|
||||||
async def _post(self, suffix, data):
|
|
||||||
res = await self._pb.post(f"https://api.bitbank.cc/v1/{suffix}", data=data)
|
|
||||||
return self._check(await res.json())
|
|
||||||
|
|
||||||
async def _get(self, suffix):
|
|
||||||
res = await self._pb.get(f"https://api.bitbank.cc/v1/{suffix}")
|
|
||||||
return self._check(await res.json())
|
|
||||||
|
|
||||||
def _check(self, json):
|
|
||||||
if "success" not in json or 1 != json["success"]:
|
|
||||||
code = json["data"]["code"]
|
|
||||||
raise Exception(f"API error: {code}")
|
|
||||||
return json
|
|
||||||
|
|
||||||
|
|
||||||
class Asset:
|
|
||||||
def __init__(self, json):
|
|
||||||
self.name = json["asset"]
|
|
||||||
self.amount = float(json["onhand_amount"])
|
|
||||||
self.locked = float(json["locked_amount"])
|
|
||||||
self.precision = int(json["amount_precision"])
|
|
||||||
|
|
||||||
class Order:
|
|
||||||
def __init__(self, json):
|
|
||||||
self.id = json["order_id"]
|
|
||||||
self.pair = json["pair"]
|
|
||||||
self.amount = float(json["start_amount"])
|
|
||||||
self.remain = float(json["remaining_amount"])
|
|
||||||
self.price = float(json["average_price"])
|
|
Loading…
x
Reference in New Issue
Block a user