Skip to content

Commit

Permalink
3.0.19
Browse files Browse the repository at this point in the history
  • Loading branch information
DogsTailFarmer committed Jan 27, 2025
1 parent 60494c2 commit 267062c
Show file tree
Hide file tree
Showing 7 changed files with 65 additions and 95 deletions.
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
## 3.0.19 - 2025-01-27
### Fix
* `on_balance_update_ex`: calculating initial balance for opposite coin in Reverse cycle

### Update
* `funds_rate_exporter.py`: optimized current price queries on `coinmarketcap`
* Functions for placing and canceling orders have been optimized
* Bump requirements

## 3.0.18 - 2025-01-18
### Update
* Change dependency model for exchanges-wrapper
Expand Down
2 changes: 1 addition & 1 deletion martin_binance/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
__author__ = "Jerry Fedorenko"
__copyright__ = "Copyright © 2021 Jerry Fedorenko aka VM"
__license__ = "MIT"
__version__ = "3.0.18"
__version__ = "3.0.19"
__maintainer__ = "Jerry Fedorenko"
__contact__ = "https://github.com/DogsTailFarmer"

Expand Down
31 changes: 11 additions & 20 deletions martin_binance/executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
__author__ = "Jerry Fedorenko"
__copyright__ = "Copyright © 2021-2025 Jerry Fedorenko aka VM"
__license__ = "MIT"
__version__ = "3.0.17"
__version__ = "3.0.19"
__maintainer__ = "Jerry Fedorenko"
__contact__ = 'https://github.com/DogsTailFarmer'
##################################################################
Expand Down Expand Up @@ -1343,6 +1343,10 @@ def calc_grid(self, over_price: Decimal, calc_avg_amount=True, **kwargs):
}

def event_grid_update(self):
if not self.orders_grid:
self.place_grid_part()
return
#
do_it = False
if ADAPTIVE_TRADE_CONDITION and self.stable_state() and not self.part_amount \
and (self.orders_grid or self.orders_hold):
Expand Down Expand Up @@ -2177,20 +2181,6 @@ def place_limit_order_check(
def on_new_ticker(self, ticker: Ticker) -> None:
# print(f"on_new_ticker:{datetime.fromtimestamp(ticker.timestamp/1000)}: last_price: {ticker.last_price}")
self.last_ticker_update = int(self.get_time())
if not self.orders_grid and not self.orders_init and self.orders_hold:
_, _buy, _amount, _price = self.orders_hold.get_first()
tcm = self.get_trading_capability_manager()
if ((_buy and _price >= tcm.get_min_buy_price(ticker.last_price)) or
(not _buy and _price <= tcm.get_max_sell_price(ticker.last_price))):
waiting_order_id = self.place_limit_order_check(
_buy,
_amount,
_price,
check=True
)
self.orders_init.append_order(waiting_order_id, _buy, _amount, _price)
del self.orders_hold.orders_list[0]
#
if (self.shift_grid_threshold and self.last_shift_time and self.get_time() -
self.last_shift_time > SHIFT_GRID_DELAY
and ((self.cycle_buy and ticker.last_price >= self.shift_grid_threshold)
Expand Down Expand Up @@ -2293,8 +2283,7 @@ def on_balance_update_ex(self, balance: Dict) -> None:

self.update_sum_amount(-delta, -deposit_add)
self.place_profit_order()
elif not self.reverse:
self.initial_first += delta
self.initial_first += delta
else:
if asset == self.f_currency:
restart = True
Expand Down Expand Up @@ -2342,8 +2331,7 @@ def on_balance_update_ex(self, balance: Dict) -> None:

self.update_sum_amount(-deposit_add, -delta)
self.place_profit_order()
elif not self.reverse:
self.initial_second += delta
self.initial_second += delta

self.debug_output()

Expand Down Expand Up @@ -2556,6 +2544,9 @@ def cancel_reverse_hold(self):
self.initial_reverse_first = self.initial_reverse_second = O_DEC
self.message_log("Cancel hold reverse cycle", color=Style.B_WHITE)

def order_init_exist(self, place_order_id: int):
return bool(self.orders_init.exist(place_order_id) or place_order_id == self.tp_wait_id)

def on_place_order_success(self, place_order_id: int, order: Order) -> None:
# print(f"on_place_order_success.place_order_id: {place_order_id}")
if self.orders_init.exist(place_order_id):
Expand Down Expand Up @@ -2584,7 +2575,7 @@ def on_place_order_success(self, place_order_id: int, order: Order) -> None:
else:
self.place_grid_part()
else:
self.message_log(f"Did not have waiting order id for {place_order_id}", logging.ERROR)
self.message_log(f"Did not have waiting order {place_order_id}", logging.ERROR)

def on_place_order_error(self, place_order_id: int, error: str) -> None:
# Check all orders on exchange if exists required
Expand Down
42 changes: 22 additions & 20 deletions martin_binance/service/funds_rate_exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
__author__ = "Jerry Fedorenko"
__copyright__ = "Copyright © 2021 Jerry Fedorenko aka VM"
__license__ = "MIT"
__version__ = "3.0.17"
__version__ = "3.0.19"
__maintainer__ = "Jerry Fedorenko"
__contact__ = 'https://github.com/DogsTailFarmer'

Expand Down Expand Up @@ -145,34 +145,36 @@ def get_rate(_currency_rate) -> {}:
replace = {
'UST': 'USDT',
'IOT': 'MIOTA',
'LUNA': 'LUNC',
'LUNA2': 'LUNA',
'TESTUSDT': 'USDT',
'TESTBTC': 'BTC'
}
headers = {'Accepts': 'application/json', 'X-CMC_PRO_API_KEY': API}
session = Session()
session.headers.update(headers)
buffer_rate = {}

for currency in _currency_rate:
_currency = replace.get(currency, currency)
price = -1
parameters = {'amount': 1, 'symbol': 'USD', 'convert': _currency}
try:
response = session.get(URL, params=parameters)
except Exception as er:
print(er)
else:
if response.status_code == 429:
time.sleep(61)
request_delay *= 1.5
try:
response = session.get(URL, params=parameters)
except Exception as er:
print(er)
if response.status_code == 200:
data = response.json()
price = data['data'][0]['quote'][_currency]['price'] or -1
price = buffer_rate.get(_currency)
if price is None:
price = -1
parameters = {'amount': 1, 'symbol': 'USD', 'convert': _currency}
try:
response = session.get(URL, params=parameters)
except Exception as er:
print(er)
else:
if response.status_code == 429:
time.sleep(61)
request_delay *= 1.5
try:
response = session.get(URL, params=parameters)
except Exception as er:
print(er)
if response.status_code == 200:
data = response.json()
price = data['data'][0]['quote'][_currency]['price'] or -1
buffer_rate[_currency] = price
_currency_rate[currency] = price
# time.sleep(request_delay)
return _currency_rate
Expand Down
72 changes: 20 additions & 52 deletions martin_binance/strategy_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
__author__ = "Jerry Fedorenko"
__copyright__ = "Copyright © 2021-2025 Jerry Fedorenko aka VM"
__license__ = "MIT"
__version__ = "3.0.17"
__version__ = "3.0.19"
__maintainer__ = "Jerry Fedorenko"
__contact__ = "https://github.com/DogsTailFarmer"

Expand Down Expand Up @@ -102,8 +102,6 @@ def __init__(self):
self.funds = {}
self.order_book = {}
self.order_id = int(datetime.now().strftime("%f%S%M"))
self.wait_order_id = [] # List of placed orders for time-out detect
self.canceled_order_id = [] # List canceled orders for time-out detect
self.trades = [] # List of trades associated with strategy (limit = TRADES_LIST_LIMIT)
self.orders = {} # {int(id): Order(), } of orders associated with strategy
self.tcm = None # TradingCapabilityManager
Expand Down Expand Up @@ -164,8 +162,6 @@ def reset_vars(self):
self.funds = {}
self.order_book = {}
self.order_id = int(datetime.now().strftime("%f%S%M"))
self.wait_order_id = [] # List of placed orders for time-out detect
self.canceled_order_id = [] # List canceled orders for time-out detect
self.trades = [] # List of trades associated with strategy (limit = TRADES_LIST_LIMIT)
self.orders = {} # Set of orders associated with strategy
self.get_buffered_funds_last_time = self.get_time()
Expand Down Expand Up @@ -258,14 +254,12 @@ def place_limit_order(self, buy: bool, amount: Decimal, price: Decimal) -> int:
self.message_log(f"Send order id:{self.order_id} for {'BUY' if buy else 'SELL'}"
f" {any2str(amount)} by {any2str(price)} = {any2str(amount * price)}",
color=Style.B_YELLOW)
self.tasks_manage(self.place_limit_order_timeout(self.order_id))
self.tasks_manage(self.create_limit_order(self.order_id, buy, any2str(amount), any2str(price)))
if self.exchange == 'huobi':
time.sleep(0.02)
return self.order_id

def cancel_order(self, order_id: int, cancel_all=False) -> None:
self.tasks_manage(self.cancel_order_timeout(order_id))
self.tasks_manage(self.cancel_order_call(order_id, cancel_all))

def message_log(self, msg: str, log_level=logging.INFO, tlg=False, color=Style.WHITE, tlg_inline=False) -> None:
Expand Down Expand Up @@ -781,8 +775,7 @@ async def fetch_order(self, _id: int, _client_order_id: str = None, _filled_upda
log_level=logging.INFO, color=Style.GREEN)
if result:
return result
self.message_log(f"Can't get status for order {_id}({_client_order_id})",
log_level=logging.WARNING)
self.message_log(f"Can't get status for order {_id}({_client_order_id})", log_level=logging.WARNING)
return {}

async def on_funds_update(self):
Expand Down Expand Up @@ -863,11 +856,7 @@ def open_orders_snapshot(self, ts=None):
self.grid_buy.update({ts or int(time.time() * 1000): pd.Series(orders_buy)})
self.grid_sell.update({ts or int(time.time() * 1000): pd.Series(orders_sell)})

async def cancel_order_call(self, _id: int, cancel_all=False, count=0):
if count == 0:
self.canceled_order_id.append(_id)
elif _id in self.canceled_order_id:
self.canceled_order_id.remove(_id)
async def cancel_order_call(self, _id: int, cancel_all=False):
_fetch_order = False
try:
if prm.MODE in ('T', 'TC'):
Expand Down Expand Up @@ -917,34 +906,18 @@ async def cancel_order_call(self, _id: int, cancel_all=False, count=0):
res = await self.fetch_order(_id, _filled_update_call=True)
if res.get('status') in ('CANCELED', 'EXPIRED_IN_MATCH'):
await self.cancel_order_handler(_id, cancel_all)
elif res.get('status') == 'FILLED':
if _id in self.canceled_order_id:
self.canceled_order_id.remove(_id)
elif not res or res.get('status') in ('NEW', 'PARTIALLY_FILLED'):
await asyncio.sleep(HEARTBEAT * count)
if count <= TRY_LIMIT:
await self.cancel_order_call(_id, cancel_all=False, count=count + 1)
else:
self.on_cancel_order_error_string(_id, 'Cancel order try limit exceeded')
else:
self.on_cancel_order_error_string(_id, 'order not canceled')

async def cancel_order_handler(self, _id, cancel_all):
if _id in self.canceled_order_id:
self.canceled_order_id.remove(_id)
self.message_log(f"Cancel order {_id} success", color=Style.GREEN)
self.message_log(f"Cancel order {_id} success", color=Style.GREEN)
self.remove_from_orders_lists([_id])
self.on_cancel_order_success(_id, cancel_all=cancel_all)
if prm.MODE == 'TC' and prm.SAVE_DS and self.start_collect:
self.open_orders_snapshot()
elif prm.MODE == 'S':
await self.on_funds_update()

async def cancel_order_timeout(self, _id):
await asyncio.sleep(ORDER_TIMEOUT)
if _id in self.canceled_order_id:
self.canceled_order_id.remove(_id)
self.on_cancel_order_error_string(_id, 'Cancel order timeout')
# await asyncio.sleep(0)

async def transfer2master(self, symbol: str, amount: str):
try:
res = await self.send_request(
Expand Down Expand Up @@ -1025,12 +998,6 @@ async def buffered_funds(self, print_info: bool = True):
self.quote_asset: FundsEntry(self.funds[self.quote_asset])}
self.on_new_funds(funds)

async def place_limit_order_timeout(self, _id):
await asyncio.sleep(ORDER_TIMEOUT)
if _id in self.wait_order_id:
self.wait_order_id.remove(_id)
self.on_place_order_error(_id, 'Place order timeout')

async def get_exchange_info(self, _request, _symbol):
"""
Refresh trading rules for pair every 10 mins
Expand Down Expand Up @@ -1118,7 +1085,6 @@ async def on_klines_update(self, _klines: {str: Klines}):
self.tasks_manage(self.aiter_candles(_klines, i), name='wss')

async def create_limit_order(self, _id: int, buy: bool, amount: str, price: str) -> None:
self.wait_order_id.append(_id)
_fetch_order = False
msg = None
try:
Expand Down Expand Up @@ -1148,14 +1114,11 @@ async def create_limit_order(self, _id: int, buy: bool, amount: str, price: str)
pass # Task cancellation should not be logged as an error
except GRPCError as ex:
status_code = ex.status
if status_code == Status.FAILED_PRECONDITION:
self.message_log(f"Order {_id}: {status_code.name}, {ex.message}")
if _id in self.wait_order_id:
# Supress call strategy handler
self.wait_order_id.remove(_id)
msg = f"Create order {_id}: {status_code.name}, {ex.message}"
if status_code in (Status.FAILED_PRECONDITION, Status.DEADLINE_EXCEEDED):
self.on_place_order_error(_id, msg)
else:
_fetch_order = True
msg = f"Create order {_id}: {status_code.name}, {ex.message}"
except Exception as _ex:
_fetch_order = True
msg = f"Exception creating order {_id}: {_ex}"
Expand All @@ -1176,14 +1139,14 @@ async def create_limit_order(self, _id: int, buy: bool, amount: str, price: str)

async def create_order_handler(self, _id, result):
# print(f"create_order_handler.result: {result}")
if _id in self.wait_order_id and not self.order_exist(result['orderId']):
self.wait_order_id.remove(_id)
if self.order_init_exist(_id) and not self.order_exist(result['orderId']):
order = Order(result)
self.orders[order.id] = order
self.on_place_order_success(_id, order)
self.message_log(
f"Order placed {order.id}({result.get('clientOrderId') or _id}) for {result.get('side')}"
f" {any2str(order.amount)} by {any2str(order.price)} = {any2str(order.amount * order.price)}",
color=Style.GREEN)
self.orders[order.id] = order

if prm.MODE == 'S':
await self.on_funds_update()
Expand All @@ -1199,8 +1162,6 @@ async def create_order_handler(self, _id, result):
if prm.SAVE_DS:
self.open_orders_snapshot()

self.on_place_order_success(_id, order)

async def on_balance_update(self):
try:
async for res in self.for_request(self.stub.on_balance_update, mr.MarketRequest, symbol=self.symbol):
Expand Down Expand Up @@ -1598,6 +1559,9 @@ async def main(self, _symbol): # /NOSONAR
print(f"Can't get active orders: {ex}")
else:
active_orders = list(map(json.loads, _active_orders.orders))
for order in active_orders:
print(f"Order: {order['orderId']}, side: {order['side']}, amount: {order['origQty']}"
f" price:{order['price']}, status: {order['status']}")
# Try load last strategy state from saved files
last_state = load_last_state(prm.LAST_STATE_FILE)
restore_state = bool(last_state)
Expand All @@ -1615,7 +1579,7 @@ async def main(self, _symbol): # /NOSONAR
cancel_orders = ast.literal_eval(json.loads(res.result))
print('Before start was canceled orders:')
for i in cancel_orders:
print(f"Order:{i['orderId']}, side:{i['side']},"
print(f"Order: {i['orderId']}, side:{i['side']},"
f" amount:{i['origQty']}, price:{i['price']}, status:{i['status']}")
print(EQUAL_STR)
except asyncio.CancelledError:
Expand Down Expand Up @@ -1820,6 +1784,10 @@ def on_cancel_order_success(self, *args, **kwargs):
def on_place_order_error(self, *args):
raise NotImplementedError

@abstractmethod
def order_init_exist(self, *args):
raise NotImplementedError

@abstractmethod
def on_place_order_success(self, *args):
raise NotImplementedError
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ dynamic = ["version", "description"]
requires-python = ">=3.9"

dependencies = [
"exchanges-wrapper>=2.1.26",
"exchanges-wrapper>=2.1.27",
"jsonpickle==3.3.0",
"psutil==6.0.0",
"requests==2.32.3",
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
exchanges-wrapper>=2.1.26
exchanges-wrapper>=2.1.27
jsonpickle==3.3.0
psutil==6.0.0
requests==2.32.3
Expand Down

0 comments on commit 267062c

Please sign in to comment.