From dd427473cb6ab9ab0da9eef08d8cdda5bb63506d Mon Sep 17 00:00:00 2001 From: andrew saintway Date: Thu, 28 Jan 2021 00:16:18 +0900 Subject: [PATCH] detect portfolio changes and estimate robot status automatically --- .gitignore | 1 + graph/main-martingale.ipynb | 241 +++++++++++++++++++++++++----------- 2 files changed, 169 insertions(+), 73 deletions(-) diff --git a/.gitignore b/.gitignore index dda1a11..b79ad21 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ graph.md *graph*.png .ipynb_checkpoints/ __pycache__/ +*.DS_Store diff --git a/graph/main-martingale.ipynb b/graph/main-martingale.ipynb index 96fdb6d..48ab401 100644 --- a/graph/main-martingale.ipynb +++ b/graph/main-martingale.ipynb @@ -275,23 +275,34 @@ "source": [ "import copy\n", "from datetime import datetime\n", + "from decimal import Decimal\n", "import os\n", "\n", "import python_bitbankcc\n", "\n", "\n", + "class Portfolio(object):\n", + " def __init__(self, portfolio):\n", + " if isinstance(portfolio, type(self)):\n", + " self.__dict__.update(portfolio.__dict__)\n", + " if isinstance(portfolio, dict):\n", + " self.__dict__.update(portfolio)\n", + " if isinstance(portfolio, list):\n", + " self.__dict__.update(dict(portfolio))\n", + "\n", + "\n", "class BookKeeper(object):\n", " def __init__(self, status):\n", - " self.status = status\n", - " self.last_transaction = None\n", - "\n", " self.pub = python_bitbankcc.public()\n", - "\n", " self.prv = python_bitbankcc.private(\n", " os.environ['BITBANK_API_KEY'],\n", " os.environ['BITBANK_API_SECRET']\n", " )\n", "\n", + " self.status = status\n", + " self.portfolio = self.get_portfolio()\n", + " self.last_transaction = None\n", + "\n", " def __enter__(self):\n", " self.fsh = open('sample-history.txt', 'a', 1)\n", " self.fth = open('transaction-history.txt', 'a', 1)\n", @@ -308,7 +319,81 @@ " 'now_buy_fiat_price': int(now_ticker.get('sell', '1')) * (1 + 0.0012)\n", " }\n", "\n", - " def buy(self, unit_amount, fiat_price=None):\n", + " def get_portfolio(self):\n", + " return Portfolio([\n", + " (asset.get('asset', ''), Decimal(asset.get('onhand_amount', '0'))) for asset in self.prv.get_asset().get('assets', [])\n", + " ])\n", + "\n", + " def estimate_status(self):\n", + " old_portfolio = self.portfolio\n", + " new_portfolio = self.get_portfolio()\n", + " self.portfolio = new_portfolio\n", + "\n", + " old_fiat_money = old_portfolio.jpy\n", + " old_amount = old_portfolio.eth\n", + " new_fiat_money = new_portfolio.jpy\n", + " new_amount = new_portfolio.eth\n", + "\n", + " if old_fiat_money > new_fiat_money and old_amount < new_amount:\n", + " amount = new_amount - old_amount\n", + " fiat_price = (old_fiat_money - new_fiat_money) / amount\n", + "\n", + " if self.status.now_buy_fiat_price is None:\n", + " return self.status\n", + " now_buy_fiat_price = Decimal(self.status.now_buy_fiat_price)\n", + " if abs(fiat_price - now_buy_fiat_price) / now_buy_fiat_price > Decimal(50/100):\n", + " return self.status\n", + "\n", + " new_status = self.estimate_status_by_buying(\n", + " int( amount / Decimal(repr(self.status.trade_unit)) ),\n", + " float( fiat_price )\n", + " )\n", + "\n", + " new_status.update(\n", + " status = {\n", + " 'sold_unit_amount': 0,\n", + " 'please_buy_unit_amount': 0\n", + " }\n", + " )\n", + "\n", + " return new_status\n", + "\n", + " if old_fiat_money < new_fiat_money and old_amount > new_amount:\n", + " amount = old_amount - new_amount\n", + " fiat_price = (new_fiat_money - old_fiat_money) / amount\n", + "\n", + " if self.status.now_sell_fiat_price is None:\n", + " return self.status\n", + " now_sell_fiat_price = Decimal(self.status.now_sell_fiat_price)\n", + " if abs(fiat_price - now_sell_fiat_price) / now_sell_fiat_price > Decimal(50/100):\n", + " return self.status\n", + "\n", + " new_status = self.estimate_status_by_selling(\n", + " int( amount / Decimal(repr(self.status.trade_unit)) ),\n", + " float( fiat_price )\n", + " )\n", + "\n", + " new_status.update(\n", + " status = {\n", + " 'please_buy_unit_amount': 0,\n", + " 'please_sell_unit_amount': 0\n", + " }\n", + " )\n", + "\n", + " if new_status.bought_unit_amount == 0:\n", + " new_status.update(\n", + " status = {\n", + " 'trade_count': status.trade_count + 1,\n", + " 'buy_count': 0,\n", + " 'sell_count': 0\n", + " }\n", + " )\n", + "\n", + " return new_status\n", + "\n", + " return self.status\n", + "\n", + " def buy_secretly(self, unit_amount, fiat_price=None):\n", " if unit_amount <= 0:\n", " return\n", " if fiat_price is None:\n", @@ -319,6 +404,24 @@ " pair='eth_jpy', price=str(fiat_price), amount=str(amount), side='buy', order_type='market'\n", " )\n", "\n", + " def buy(self, unit_amount, fiat_price=None):\n", + " if unit_amount <= 0:\n", + " return\n", + " if fiat_price is None:\n", + " fiat_price = self.status.now_buy_fiat_price\n", + "\n", + " self.buy_secretly(unit_amount, fiat_price)\n", + "\n", + " return self.estimate_status_by_buying(unit_amount, fiat_price)\n", + "\n", + " def estimate_status_by_buying(self, unit_amount, fiat_price=None):\n", + " if unit_amount <= 0:\n", + " return\n", + " if fiat_price is None:\n", + " fiat_price = self.status.now_buy_fiat_price\n", + "\n", + " amount = self.status.trade_unit * unit_amount\n", + "\n", " old_status = self.status\n", " new_status = copy.deepcopy(old_status)\n", "\n", @@ -339,7 +442,7 @@ "\n", " return new_status\n", "\n", - " def sell(self, unit_amount, fiat_price=None):\n", + " def sell_secretly(self, unit_amount, fiat_price=None):\n", " if unit_amount <= 0:\n", " return\n", " if fiat_price is None:\n", @@ -350,6 +453,24 @@ " pair='eth_jpy', price=str(fiat_price), amount=str(amount), side='sell', order_type='market'\n", " )\n", "\n", + " def sell(self, unit_amount, fiat_price=None):\n", + " if unit_amount <= 0:\n", + " return\n", + " if fiat_price is None:\n", + " fiat_price = self.status.now_sell_fiat_price\n", + "\n", + " self.sell_secretly(unit_amount, fiat_price)\n", + "\n", + " return self.estimate_status_by_selling(unit_amount, fiat_price)\n", + "\n", + " def estimate_status_by_selling(self, unit_amount, fiat_price=None):\n", + " if unit_amount <= 0:\n", + " return\n", + " if fiat_price is None:\n", + " fiat_price = self.status.now_sell_fiat_price\n", + "\n", + " amount = self.status.trade_unit * unit_amount\n", + "\n", " old_status = self.status\n", " new_status = copy.deepcopy(old_status)\n", "\n", @@ -450,21 +571,42 @@ "import time\n", "from decimal import Decimal\n", "with BookKeeper(status) as bookkeeper:\n", - " from unittest.mock import Mock\n", - " bookkeeper.prv = Mock()\n", + "# from unittest.mock import Mock\n", + "# bookkeeper.prv.order = Mock()\n", " while True:\n", - "# bought_unit_amounts = [\n", - "# int(Decimal(asset.get('free_amount', '0')) / Decimal(repr(TRADE_UNIT))) for asset in prv.get_asset().get('assets', []) \\\n", - "# if asset.get('asset', '') == 'eth'\n", - "# ] if not isinstance(prv, Mock) else []\n", + " new_status = bookkeeper.estimate_status()\n", + " if new_status is not status:\n", + " bookkeeper.fth.write(f'{new_status}\\n')\n", + " new_status.write()\n", + "\n", + " send_slack(\n", + " f'{bookkeeper.last_transaction}'\n", + " f'BY {new_status.get_robot_title()}',\n", + " 'Power by https://jhub.name/', 'good' if new_status.total_gained_fiat_money > 0 else 'danger'\n", + " )\n", + "\n", + " send_slack(\n", + " f'{new_status} => Support level is {MIN_UNIT_CC_INIT_TRADE_JPY} JPY.',\n", + " 'Power by https://jhub.name/', 'good' if new_status.get_total_gain_fiat_money() > 0 else 'danger'\n", + " )\n", + "\n", + " if new_status.bought_unit_amount == 0 and new_status.total_gained_fiat_money > MAX_GAIN_JPY << 2 and not isinstance(bookkeeper.prv, Mock):\n", + " from unittest.mock import Mock\n", + " bookkeeper.prv = Mock()\n", + " send_slack(\n", + " f'{new_status.robot_name} is turned to Mock', 'Power by https://jhub.name/', 'good'\n", + " )\n", "\n", - "# if len(bought_unit_amounts) > 0:\n", - "# bought_unit_amount_before = status.bought_unit_amount\n", + " if new_status.total_gained_fiat_money < -MAX_TOTAL_LOST_JPY:\n", + " break\n", "\n", - "# if bought_unit_amount_before > 0:\n", - "# status.bought_unit_amount = bought_unit_amounts[0]\n", - "# used_jpy *= status.bought_unit_amount / bought_unit_amount_before\n", + " if new_status.total_gained_fiat_money == status.total_gained_fiat_money:\n", + " time.sleep(SAMPLE_INTERVAL / TEST_RATIO)\n", + " else:\n", + " time.sleep(new_status.cooling_time/TEST_RATIO)\n", + " status.cooling_time = HAPPY_COOLING_TIME\n", "\n", + " status.update(new_status)\n", " status.update(bookkeeper.get_price())\n", "\n", " if status.sample_number % TRADE_RATE == 0 and status.bought_average_fiat_price is not None and status.now_sell_fiat_price > status.bought_average_fiat_price:\n", @@ -507,49 +649,8 @@ " status.cooling_time = SAD_COOLING_TIME\n", "\n", " if status.please_sell_unit_amount > 0:\n", - " new_status = bookkeeper.sell(status.please_sell_unit_amount)\n", - " bookkeeper.fth.write(f'{new_status}\\n')\n", - "\n", + " bookkeeper.sell_secretly(status.please_sell_unit_amount)\n", " bookkeeper.fsh.write(f'{status}\\n')\n", - "\n", - " send_slack(\n", - " f'{bookkeeper.last_transaction}'\n", - " f'BY {new_status.get_robot_title()}',\n", - " 'Power by https://jhub.name/', 'good' if new_status.total_gained_fiat_money > 0 else 'danger'\n", - " )\n", - "\n", - " status.update( status = new_status )\n", - " status.update(\n", - " status = {\n", - " 'please_buy_unit_amount': 0,\n", - " 'please_sell_unit_amount': 0\n", - " }\n", - " )\n", - "\n", - " if status.bought_unit_amount == 0:\n", - " status.update(\n", - " status = {\n", - " 'trade_count': status.trade_count + 1,\n", - " 'buy_count': 0,\n", - " 'sell_count': 0\n", - " }\n", - " )\n", - "\n", - " if status.bought_unit_amount == 0 and status.total_gained_fiat_money > MAX_GAIN_JPY << 2 and not isinstance(bookkeeper.prv, Mock):\n", - " from unittest.mock import Mock\n", - " bookkeeper.prv = Mock()\n", - " send_slack(\n", - " f'{status.robot_name} is turned to Mock', 'Power by https://jhub.name/', 'good'\n", - " )\n", - "\n", - " status.write()\n", - "\n", - " if status.total_gained_fiat_money < -MAX_TOTAL_LOST_JPY:\n", - " break\n", - "\n", - " status.sample_number += 1\n", - " time.sleep(status.cooling_time/TEST_RATIO)\n", - " status.cooling_time = HAPPY_COOLING_TIME\n", " continue\n", "\n", " if status.used_fiat_money < INIT_BUY_JPY and INIT_BUY_JPY - status.used_fiat_money <= (MAX_TRADED_JPY - status.used_fiat_money) * 0.75 \\\n", @@ -566,20 +667,17 @@ " status.please_buy_unit_amount = trade_unit_amount\n", "\n", " if status.please_buy_unit_amount > 0:\n", - " new_status = bookkeeper.buy(status.please_buy_unit_amount)\n", - " bookkeeper.fth.write(f'{new_status}\\n')\n", + " bookkeeper.buy_secretly(status.please_buy_unit_amount)\n", + " bookkeeper.fsh.write(f'{status}\\n')\n", + " continue\n", "\n", - " status.update( status = new_status )\n", - " status.update(\n", - " status = {\n", - " 'sold_unit_amount': 0,\n", - " 'please_buy_unit_amount': 0\n", - " }\n", - " )\n", - " elif status.sample_number % TRADE_RATE == 0:\n", + " bookkeeper.fsh.write(f'{status}\\n')\n", + "\n", + " if status.sample_number % TRADE_RATE == 0:\n", " bookkeeper.fth.write(f'{status}\\n')\n", "\n", - " bookkeeper.fsh.write(f'{status}\\n')\n", + " if status.sample_number % BACKUP_RATE == 0:\n", + " status.write()\n", "\n", " if status.sample_number % NOTIFY_RATE == 0:\n", " send_slack(\n", @@ -587,9 +685,6 @@ " 'Power by https://jhub.name/', 'good' if status.get_total_gain_fiat_money() > 0 else 'danger'\n", " )\n", "\n", - " if status.sample_number % BACKUP_RATE == 0:\n", - " status.write()\n", - "\n", " status.sample_number += 1\n", " time.sleep(SAMPLE_INTERVAL / TEST_RATIO)" ]