From 18e1199e625cbf32c5f32ccc95eba30d5e5b5c2d Mon Sep 17 00:00:00 2001 From: andrew saintway Date: Sun, 31 Jan 2021 14:44:54 +0900 Subject: [PATCH] lossable_unit_cc_bought_jpy -> lossable_unit_cc_bought_ratio --- README.ipynb | 24 +++-- graph/main-martingale.ipynb | 193 ++++++++++++++++++++++++------------ 2 files changed, 148 insertions(+), 69 deletions(-) diff --git a/README.ipynb b/README.ipynb index 205e99a..b5d1c69 100644 --- a/README.ipynb +++ b/README.ipynb @@ -242,9 +242,9 @@ "text/plain": [ "{'assets': [{'asset': 'jpy',\n", " 'amount_precision': 4,\n", - " 'onhand_amount': '502004.0359',\n", + " 'onhand_amount': '347199.5639',\n", " 'locked_amount': '0.0000',\n", - " 'free_amount': '502004.0359',\n", + " 'free_amount': '347199.5639',\n", " 'stop_deposit': False,\n", " 'stop_withdrawal': False,\n", " 'withdrawal_fee': {'threshold': '30000.0000',\n", @@ -276,9 +276,9 @@ " 'withdrawal_fee': '0.150000'},\n", " {'asset': 'eth',\n", " 'amount_precision': 8,\n", - " 'onhand_amount': '0.00000000',\n", - " 'locked_amount': '0.00000000',\n", - " 'free_amount': '0.00000000',\n", + " 'onhand_amount': '0.02470000',\n", + " 'locked_amount': '0.02460000',\n", + " 'free_amount': '0.00010000',\n", " 'stop_deposit': False,\n", " 'stop_withdrawal': False,\n", " 'withdrawal_fee': '0.00500000'},\n", @@ -300,12 +300,20 @@ " 'withdrawal_fee': '0.00100000'},\n", " {'asset': 'xlm',\n", " 'amount_precision': 7,\n", - " 'onhand_amount': '0.0005000',\n", + " 'onhand_amount': '0.0000000',\n", " 'locked_amount': '0.0000000',\n", - " 'free_amount': '0.0005000',\n", + " 'free_amount': '0.0000000',\n", + " 'stop_deposit': False,\n", + " 'stop_withdrawal': False,\n", + " 'withdrawal_fee': '0.0100000'},\n", + " {'asset': 'qtum',\n", + " 'amount_precision': 8,\n", + " 'onhand_amount': '0.00000000',\n", + " 'locked_amount': '0.00000000',\n", + " 'free_amount': '0.00000000',\n", " 'stop_deposit': False,\n", " 'stop_withdrawal': False,\n", - " 'withdrawal_fee': '0.0100000'}]}" + " 'withdrawal_fee': '0.01000000'}]}" ] }, "execution_count": 4, diff --git a/graph/main-martingale.ipynb b/graph/main-martingale.ipynb index f640048..c7fd9ea 100644 --- a/graph/main-martingale.ipynb +++ b/graph/main-martingale.ipynb @@ -22,6 +22,8 @@ "metadata": {}, "outputs": [], "source": [ + "from decimal import Decimal\n", + "\n", "SAMPLE_INTERVAL = 60\n", "HAPPY_COOLING_TIME = 30\n", "SAD_COOLING_TIME = 60 * 6\n", @@ -30,19 +32,19 @@ "TRADE_RATE = 10 << 10\n", "BACKUP_RATE = 1\n", "\n", - "TRADE_UNIT = 0.0001\n", - "MAX_BOUGHT_JPY = 1350000\n", + "TRADE_UNIT = Decimal('0.0001')\n", "\n", "GAINABLE_UNIT_CC_SOLD_JPY = 25000 # TODO: self-adaptive by trend analysis\n", - "LOSSABLE_UNIT_CC_BOUGHT_JPY = 125000\n", + "LOSSABLE_UNIT_CC_BOUGHT_RATIO = Decimal('0.9')\n", "\n", "MIN_UNIT_CC_TRADE_JPY = 5000\n", "MAX_UNIT_CC_TRADE_JPY = MIN_UNIT_CC_TRADE_JPY << 10\n", "LOSSABLE_UNIT_CC_SELL_JPY = MAX_UNIT_CC_TRADE_JPY\n", "\n", - "MAX_GAIN_JPY = MAX_BOUGHT_JPY << 10 # useless\n", - "MAX_LOSS_JPY = MAX_BOUGHT_JPY << 10 # useless\n", - "MAX_LOST_JPY = MAX_BOUGHT_JPY << 10" + "import sys\n", + "MAX_GAIN_JPY = sys.maxsize # useless\n", + "MAX_LOSS_JPY = sys.maxsize # useless\n", + "MAX_LOST_JPY = sys.maxsize" ] }, { @@ -51,11 +53,7 @@ "metadata": {}, "outputs": [], "source": [ - "import math\n", - "MIN_UNIT_CC_BOUGHT_JPY = 15000\n", - "INIT_SELL_JPY = 50\n", - "INIT_BUY_JPY = MAX_BOUGHT_JPY / math.exp(LOSSABLE_UNIT_CC_BOUGHT_JPY / (MIN_UNIT_CC_BOUGHT_JPY - MIN_UNIT_CC_TRADE_JPY))\n", - "INIT_BUY_JPY" + "INIT_SELL_UNIT_AMOUNT = 1" ] }, { @@ -81,6 +79,8 @@ "metadata": {}, "outputs": [], "source": [ + "import copy\n", + "from decimal import Decimal\n", "import os\n", "import yaml\n", "\n", @@ -94,6 +94,7 @@ " self.robot_name = robot_name\n", " self.trade_unit = TRADE_UNIT\n", "\n", + " self.unused_fiat_money = None\n", " self.now_buy_fiat_price = None\n", " self.now_sell_fiat_price = None\n", " self.please_buy_unit_amount = 0\n", @@ -111,8 +112,12 @@ "\n", " self.total_gained_fiat_money = 0\n", "\n", + " def get_max_used_fiat_money(self):\n", + " return self.unused_fiat_money + self.used_fiat_money\n", + "# return self.unused_fiat_money + self.total_gained_fiat_money\n", + "\n", " def get_usage(self):\n", - " return self.used_fiat_money / MAX_BOUGHT_JPY\n", + " return self.used_fiat_money / self.get_max_used_fiat_money()\n", "\n", " def get_robot_title(self):\n", " robot_status_description = f'{self.get_usage() * 100:.2f}%'\n", @@ -136,6 +141,9 @@ " f'SELL {self.bought_amount:.4f} ETH for {sell_fiat_price_description} JPY/ETH. BUY for {self.now_buy_fiat_price:.2f} JPY/ETH.\\n'\n", " )\n", "\n", + " def get_max_sold_unit_amount(self):\n", + " return self.bought_unit_amount + self.sold_unit_amount\n", + "\n", " def estimate_gained_fiat_money(self, sell_unit_amount, sell_fiat_price=None):\n", " if sell_unit_amount <= 0:\n", " return\n", @@ -154,11 +162,18 @@ " self.__dict__.update(\n", " yaml.load(f, Loader=yaml.FullLoader)\n", " )\n", + " for i in self.__dict__:\n", + " if isinstance(self.__dict__[i], float):\n", + " self.__dict__[i] = Decimal(repr(self.__dict__[i]))\n", "\n", " def write(self):\n", - " with open(type(self).TMPFILE, 'w') as f:\n", - " yaml.dump(self.__dict__, f, sort_keys=False)\n", - " os.replace(type(self).TMPFILE, type(self).STATUS_FILE)\n", + " this = copy.deepcopy(self)\n", + " for i in this.__dict__:\n", + " if isinstance(this.__dict__[i], Decimal):\n", + " this.__dict__[i] = float(this.__dict__[i])\n", + " with open(type(this).TMPFILE, 'w') as f:\n", + " yaml.dump(this.__dict__, f, sort_keys=False)\n", + " os.replace(type(this).TMPFILE, type(this).STATUS_FILE)\n", "\n", " def clear_bought_status(self):\n", " self.bought_unit_amount = 0\n", @@ -312,6 +327,7 @@ "\n", " self.status = status\n", " self.portfolio = self.get_portfolio()\n", + " self.status.unused_fiat_money = self.portfolio.jpy\n", " self.last_transaction = None\n", "\n", " def __enter__(self):\n", @@ -326,16 +342,16 @@ " def get_price(self):\n", " now_ticker = self.pub.get_ticker('eth_jpy')\n", " return {\n", - " 'now_sell_fiat_price': int(now_ticker.get('buy', '0')) * (1 - 0.0012),\n", - " 'now_buy_fiat_price': int(now_ticker.get('sell', '1')) * (1 + 0.0012)\n", + " 'now_sell_fiat_price': Decimal(now_ticker.get('buy', '0')) * (1 - Decimal('0.0012')),\n", + " 'now_buy_fiat_price': Decimal(now_ticker.get('sell', '1')) * (1 + Decimal('0.0012'))\n", " }\n", "# import requests\n", "# while True:\n", "# try:\n", "# now_ticker = self.pub.get_ticker('eth_jpy')\n", "# return {\n", - "# 'now_sell_fiat_price': int(now_ticker.get('buy', '0')) * (1 - 0.0012),\n", - "# 'now_buy_fiat_price': int(now_ticker.get('sell', '1')) * (1 + 0.0012)\n", + "# 'now_sell_fiat_price': Decimal(now_ticker.get('buy', '0')) * (1 - Decimal('0.0012')),\n", + "# 'now_buy_fiat_price': Decimal(now_ticker.get('sell', '1')) * (1 + Decimal('0.0012'))\n", "# }\n", "# except requests.exceptions.ConnectionError:\n", "# import time\n", @@ -343,13 +359,14 @@ "\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", + " (asset.get('asset', ''), Decimal(asset.get('free_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", + " self.status.unused_fiat_money = self.portfolio.jpy\n", "\n", " old_fiat_money = old_portfolio.jpy\n", " old_amount = old_portfolio.eth\n", @@ -360,15 +377,15 @@ " 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", + " now_buy_fiat_price = self.status.now_buy_fiat_price\n", + " if 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", + " 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", + " amount / self.status.trade_unit,\n", + " fiat_price\n", " )\n", "\n", " new_status.update(\n", @@ -384,15 +401,15 @@ " 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", + " now_sell_fiat_price = self.status.now_sell_fiat_price\n", + " if 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", + " amount / self.status.trade_unit,\n", + " fiat_price\n", " )\n", "\n", " new_status.update(\n", @@ -405,7 +422,7 @@ " if new_status.bought_unit_amount == 0:\n", " new_status.update(\n", " status = {\n", - " 'trade_count': status.trade_count + 1,\n", + " 'trade_count': new_status.trade_count + 1,\n", " 'buy_count': 0,\n", " 'sell_count': 0\n", " }\n", @@ -413,6 +430,15 @@ "\n", " return new_status\n", "\n", + " if old_fiat_money == new_fiat_money and old_amount > new_amount:\n", + " new_status = copy.deepcopy(self.status)\n", + "\n", + " new_status.bought_unit_amount *= new_amount / old_amount\n", + " new_status.unused_fiat_money *= new_amount / old_amount\n", + " new_status.trim_bought_status()\n", + "\n", + " return new_status\n", + "\n", " return self.status\n", "\n", " def buy_secretly(self, unit_amount, fiat_price=None):\n", @@ -548,21 +574,45 @@ "from scipy import optimize as opt\n", "from numpy.lib import scimath\n", "\n", - "def get_trade_unit_amount(now_trade_jpy, traded_unit_amount, traded_jpy, mutable_unit_cc_traded_jpy, max_traded_jpy, init_jpy):\n", - " if traded_unit_amount == 0:\n", - " return 1\n", - " avg_jpy = traded_jpy / (TRADE_UNIT * traded_unit_amount)\n", - " diff_jpy = abs(now_trade_jpy - avg_jpy)\n", - " min_diff_jpy = mutable_unit_cc_traded_jpy / scimath.log(max_traded_jpy / init_jpy)\n", + "def get_buy_unit_amount(status):\n", + " used_fiat_money = float(status.used_fiat_money)\n", + " unit_cc_diff_jpy = float(abs(status.bought_average_fiat_price - status.now_buy_fiat_price))\n", + "\n", + " lossable_unit_cc_bought_jpy = (status.bought_average_fiat_price - MIN_UNIT_CC_TRADE_JPY) * LOSSABLE_UNIT_CC_BOUGHT_RATIO\n", + " min_unit_cc_diff_jpy = float(lossable_unit_cc_bought_jpy) / scimath.log(float(status.get_max_used_fiat_money() / status.used_fiat_money))\n", + " status.min_bought_average_fiat_price = status.bought_average_fiat_price - lossable_unit_cc_bought_jpy\n", + "\n", + " bought_average_fiat_price = float(status.bought_average_fiat_price)\n", + " now_buy_fiat_price = float(status.now_buy_fiat_price)\n", + " trade_unit = float(status.trade_unit)\n", + "\n", " return math.ceil(\n", - " traded_jpy * opt.fsolve(\n", + " used_fiat_money * opt.fsolve(\n", " lambda c: [\n", " (\n", - " diff_jpy - min_diff_jpy * scimath.log(1 + c[0]) / (avg_jpy * c[0]) * (avg_jpy * c[0] + now_trade_jpy)\n", + " unit_cc_diff_jpy - min_unit_cc_diff_jpy * scimath.log(1 + c[0]) / (bought_average_fiat_price * c[0]) * (bought_average_fiat_price * c[0] + now_buy_fiat_price)\n", " ).real\n", " ],\n", " [-0.5]\n", - " )[0] / now_trade_jpy / TRADE_UNIT\n", + " )[0] / now_buy_fiat_price / trade_unit\n", + " )\n", + "\n", + "def get_sell_unit_amount(status):\n", + " sold_unit_amount = float(status.sold_unit_amount)\n", + " unit_cc_diff_jpy = float(abs(status.now_sell_fiat_price - status.sold_average_fiat_price))\n", + "\n", + " min_unit_cc_diff_jpy = float(GAINABLE_UNIT_CC_SOLD_JPY) / scimath.log(float(status.get_max_sold_unit_amount() / status.sold_unit_amount))\n", + " status.max_sold_average_fiat_price = status.sold_average_fiat_price + GAINABLE_UNIT_CC_SOLD_JPY\n", + "\n", + " return math.ceil(\n", + " sold_unit_amount * opt.fsolve(\n", + " lambda c: [\n", + " (\n", + " unit_cc_diff_jpy - min_unit_cc_diff_jpy * scimath.log(1 + c[0]) / c[0] * (c[0] + 1)\n", + " ).real\n", + " ],\n", + " [-0.5]\n", + " )[0]\n", " )" ] }, @@ -573,17 +623,6 @@ "## Start Trading" ] }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "send_slack(\n", - " f'{status.robot_name} started\\n{status.get_robot_title()}', 'Power by https://jhub.name/', 'good'\n", - ")" - ] - }, { "cell_type": "code", "execution_count": null, @@ -592,9 +631,13 @@ "source": [ "import time\n", "from decimal import Decimal\n", + "import math\n", "with BookKeeper(status) as bookkeeper:\n", "# from unittest.mock import Mock\n", "# bookkeeper.prv.order = Mock()\n", + " send_slack(\n", + " f'{status.robot_name} started\\n{status.get_robot_title()}', 'Power by https://jhub.name/', 'good'\n", + " )\n", " while True:\n", " new_status = bookkeeper.estimate_status()\n", " if new_status is not status:\n", @@ -627,7 +670,7 @@ " 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", + " new_status.cooling_time = HAPPY_COOLING_TIME\n", "\n", " status.update(new_status)\n", " status.update(bookkeeper.get_price())\n", @@ -638,14 +681,11 @@ "\n", " if status.sold_unit_amount == 0 and status.bought_average_fiat_price is not None and status.now_sell_fiat_price > status.bought_average_fiat_price:\n", " status.sample_number = 0\n", - " status.please_sell_unit_amount = min(math.ceil(INIT_SELL_JPY / status.now_sell_fiat_price / status.trade_unit), status.bought_unit_amount)\n", + " status.please_sell_unit_amount = min(INIT_SELL_UNIT_AMOUNT, status.bought_unit_amount)\n", " status.cooling_time = HAPPY_COOLING_TIME\n", "\n", " if status.sold_unit_amount > 0 and status.now_sell_fiat_price > status.sold_average_fiat_price:\n", - " trade_unit_amount = min(\n", - " get_trade_unit_amount(status.now_sell_fiat_price, status.sold_unit_amount, status.got_fiat_money, GAINABLE_UNIT_CC_SOLD_JPY, status.used_fiat_money + status.got_fiat_money, INIT_SELL_JPY),\n", - " status.bought_unit_amount\n", - " )\n", + " trade_unit_amount = min(get_sell_unit_amount(status), status.bought_unit_amount)\n", " if trade_unit_amount > 0:\n", " status.sample_number = 0\n", " status.please_sell_unit_amount = trade_unit_amount\n", @@ -676,14 +716,30 @@ " bookkeeper.fsh.write(f'{status}\\n')\n", " continue\n", "\n", - " if status.used_fiat_money < INIT_BUY_JPY and INIT_BUY_JPY - status.used_fiat_money <= (MAX_BOUGHT_JPY - status.used_fiat_money) * 0.75 \\\n", + "# new_status = bookkeeper.sell(status.please_sell_unit_amount)\n", + "# bookkeeper.fth.write(f'{new_status}\\n')\n", + "# bookkeeper.fsh.write(f'{new_status}\\n')\n", + "# status.update(new_status)\n", + "# status.update(\n", + "# status = {\n", + "# 'please_buy_unit_amount': 0,\n", + "# 'please_sell_unit_amount': 0\n", + "# }\n", + "# )\n", + "# status.write()\n", + "# status.sample_number += 1\n", + "# time.sleep(SAMPLE_INTERVAL / TEST_RATIO)\n", + "# continue\n", + "\n", + " init_buy_jpy = status.get_max_used_fiat_money() / Decimal(math.exp(LOSSABLE_UNIT_CC_BOUGHT_RATIO / (1 - LOSSABLE_UNIT_CC_BOUGHT_RATIO)))\n", + " if status.used_fiat_money < init_buy_jpy and init_buy_jpy - status.used_fiat_money <= (status.get_max_used_fiat_money() - status.used_fiat_money) * Decimal('0.75') \\\n", " and status.now_buy_fiat_price > MIN_UNIT_CC_TRADE_JPY and status.now_buy_fiat_price < MAX_UNIT_CC_TRADE_JPY:\n", " status.sample_number = 0\n", - " status.please_buy_unit_amount = math.ceil((INIT_BUY_JPY - status.used_fiat_money) / status.now_buy_fiat_price / status.trade_unit)\n", + " status.please_buy_unit_amount = math.ceil((init_buy_jpy - status.used_fiat_money) / status.now_buy_fiat_price / status.trade_unit)\n", "\n", - " if status.used_fiat_money >= INIT_BUY_JPY and status.bought_average_fiat_price is not None and status.now_buy_fiat_price < status.bought_average_fiat_price:\n", - " trade_unit_amount = get_trade_unit_amount(status.now_buy_fiat_price, status.bought_unit_amount, status.used_fiat_money, LOSSABLE_UNIT_CC_BOUGHT_JPY, MAX_BOUGHT_JPY, INIT_BUY_JPY)\n", - " while trade_unit_amount > 0 and status.now_buy_fiat_price * status.trade_unit * trade_unit_amount > (MAX_BOUGHT_JPY - status.used_fiat_money) * 0.75:\n", + " if status.used_fiat_money >= init_buy_jpy and status.bought_average_fiat_price is not None and status.now_buy_fiat_price < status.bought_average_fiat_price:\n", + " trade_unit_amount = get_buy_unit_amount(status)\n", + " while trade_unit_amount > 0 and status.now_buy_fiat_price * status.trade_unit * trade_unit_amount > (status.get_max_used_fiat_money() - status.used_fiat_money) * Decimal('0.75'):\n", " trade_unit_amount >>= 1\n", " if trade_unit_amount > 0:\n", " status.sample_number = 0\n", @@ -694,6 +750,21 @@ " bookkeeper.fsh.write(f'{status}\\n')\n", " continue\n", "\n", + "# new_status = bookkeeper.buy(status.please_buy_unit_amount)\n", + "# bookkeeper.fth.write(f'{new_status}\\n')\n", + "# bookkeeper.fsh.write(f'{new_status}\\n')\n", + "# status.update(new_status)\n", + "# status.update(\n", + "# status = {\n", + "# 'sold_unit_amount': 0,\n", + "# 'please_buy_unit_amount': 0\n", + "# }\n", + "# )\n", + "# status.write()\n", + "# status.sample_number += 1\n", + "# time.sleep(SAMPLE_INTERVAL / TEST_RATIO)\n", + "# continue\n", + "\n", " bookkeeper.fsh.write(f'{status}\\n')\n", "\n", " if status.sample_number % TRADE_RATE == 0:\n",