Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BB] Improve order management #34 #50

Merged
merged 5 commits into from
May 22, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
147 changes: 84 additions & 63 deletions strategies/bollinger_bands_strategy.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
from datetime import datetime
import math
from api import Api
from api import Api, ApiException
import time

from talipp.indicators import BB

class bollinger_bands_strategy:
def __init__(self, api_client, CONFIG, logger):
logger.info(" > init: bollinger_bands_strategy instance created.")

logger.info("========================================================================")
logger.info(" ")
logger.info(" ⚠️ WARNING! ⚠️ ")
Expand All @@ -22,16 +22,11 @@ def __init__(self, api_client, CONFIG, logger):
logger.info("========================================================================")

# Internal state:
self.start_time = datetime.now()
self.counter = 0
self.last_execution_time = None
self.last_order_ref=None
self._values = (None, None)
self.api_client=api_client
self.logger=logger
self.api_client : Api = api_client
self.logger = logger
self.initialized = False
self.sell_order_ref = None
self.buy_order_ref = None
self.last_candle = None

# Strategy configuration:
Expand All @@ -55,20 +50,6 @@ def __init__(self, api_client, CONFIG, logger):
def place_buy_order(self, api_client, logger, price):
logger.info(" ⚙️ Placing BUY order...")

if not self.buy_order_ref == None:
logger.info(f" > Already placed BUY order. Nothing to do.")
return

if self.sell_order_ref == None:
logger.debug(f" > No SELL order. Nothing to cancel.")
else:
try:
response = api_client.cancel_order(self.sell_order_ref)
logger.info(f" > [OK] Canceled SELL order: {self.sell_order_ref}")
self.sell_order_ref = None
except:
logger.exception(f" > ⚠️ [FAILED] could not cancel order: {self.sell_order_ref} ⚠️")
logger.error(f" > Exception! ")
try:
balance_available = int(api_client.get_balances().get(self.base_asset, 0))
logger.debug(f" > balance_available : {balance_available}")
Expand All @@ -89,29 +70,13 @@ def place_buy_order(self, api_client, logger, price):
price_amount=f"{int(math.floor(offered_amount / price))}"
)
logger.info(f" > [OK] PLACED NEW BUY ORDER: {response.order_ref}")
self.buy_order_ref=response.order_ref
except:
logger.error(" > ⚠️ [FAILED] Could not place BUY order. ⚠️")
logger.exception(f" > Exception! ")

def place_sell_order(self, api_client, logger, price):
logger.info("Placing SELL order...")

if not self.sell_order_ref == None:
logger.info(f" > Already placed SELL order. Nothing to do.")
return
logger.info(" ⚙️ Placing SELL order...")

if self.buy_order_ref == None:
logger.debug(f" > No BUY order placed. Nothing to cancel.")
else:
try:
logger.info(f" ⚙️ Canceling BUY order: {self.buy_order_ref}")
response = api_client.cancel_order(self.buy_order_ref)
logger.info(f" > [OK] Canceled BUY order: {self.buy_order_ref}")
self.buy_order_ref = None
except:
logger.error(f" > ⚠️ [FAILED] could not cancel order: {self.buy_order_ref} ⚠️")
logger.exception(f" > Exception! ")
try:
balance_available = int(api_client.get_balances().get(self.target_asset, 0))
logger.info(f" > balance_available : {balance_available}")
Expand All @@ -130,14 +95,56 @@ def place_sell_order(self, api_client, logger, price):
price_amount=f"{int(math.floor(order_size * price))}"
)
logger.info(f" > [OK] PLACED NEW SELL ORDER: {response.order_ref}")
self.sell_order_ref=response.order_ref
except:
logger.error(f" > ⚠️ [FAILED] Could not place SELL order. ⚠️")
logger.exception(f" > Exception! ")

def get_buy_orders(self):
own_orders = self.api_client.get_own_orders(self.market)
return own_orders.bids

def get_sell_orders(self):
own_orders = self.api_client.get_own_orders(self.market)
return own_orders.asks

def cancel_buy_orders(self):
self.logger.info(" > Cancel all BUY orders...")
self.cancel_orders("bid")
self.logger.info(" > [OK] Canceled all BUY orders.")

def cancel_sell_orders(self):
self.logger.info(" > Cancel all SELL orders...")
self.cancel_orders("ask")
self.logger.info(" > [OK] Canceled all SELL orders.")

def cancel_orders(self, side):
while True:
orders = []
own_orders = self.api_client.get_own_orders(self.market)
if (side == "ask"):
orders = own_orders.asks
else:
orders = own_orders.bids

if len(orders) == 0:
return
else:
self.logger.info(f" Remaining {side} orders: {len(orders)}.")

order = orders[0]
try:
self.logger.info(f" ⚙️ Canceling order: {order.output_reference}")
self.api_client.cancel_order(order.output_reference)
self.logger.info(f" > [OK] Canceled order: {order.output_reference}")
except ApiException:
self.logger.error(f" > ⚠️ [FAILED] could not cancel order: {order.output_reference} ⚠️")
self.logger.exception(f" > Exception! ")

def process_candle(self, candle):
self.logger.info(f"--------------------------------------------------------------------------------")
self.logger.info(f" > processsing candle - timestamp: {candle.timestamp} - base_close: {candle.base_close}")
if self.initialized:
self.logger.info(f" > processsing candle - timestamp: {candle.timestamp} - base_close: {candle.base_close}")
else:
self.logger.info(f" > processsing init candle - timestamp: {candle.timestamp} - base_close: {candle.base_close}")

if (not self.last_candle == None) and (self.last_candle.timestamp == candle.timestamp):
self.logger.info(f" > Candle has already been processsed. Nothing to do.")
Expand All @@ -152,48 +159,68 @@ def process_candle(self, candle):
# Keep a small window of values to check if there is a crossover.
self._values = (self._values[-1], value)

self.logger.debug(f" > self.bb.input_values: {self.bb.input_values}")

if len(self.bb) < 2 or self.bb[-1] == None or self.bb[-2] == None:
self.logger.info(f" Bollinger Bands: Initializing... ⚙️ ⏳ ")
self.logger.info(f" BOLLINGER BANDS: Initializing... ⚙️ ⏳ ")
self.logger.info(f" > Upper band: Not available.")
self.logger.info(f" > Lower band: Not available.")
self.logger.info(f" Orders: ")
self.logger.info(f" > On-Chain BUY order: {self.buy_order_ref} ")
self.logger.info(f" > On-Chain SELL order: {self.sell_order_ref} ")
return

self.logger.info(f" Bollinger Bands: ")
self.logger.info(f" BOLLINGER BANDS: ")
self.logger.info(f" > Upper band: {self.bb[-1].ub}")
self.logger.info(f" > Lower band: {self.bb[-1].lb}")

if self.initialized == False:
self.logger.info(f" -> Initializaion phase. Do not place orders yet.")
return

self.place_buy_order(self.api_client, self.logger, candle.base_close)

# Price moved below lower band ?
if self._values[-2] >= self.bb[-2].lb and self._values[-1] < self.bb[-1].lb:
self.logger.info(f" -> Price moved below the lower band -> BUY! 🛒 🛒 🛒 ")
self.place_buy_order(self.api_client, self.logger, candle.base_close)
self.cancel_sell_orders()
if len(self.get_buy_orders()) > 0:
self.logger.info(" > Already placed BUY order. Nothing to do.")
else:
self.place_buy_order(self.api_client, self.logger, candle.base_close)
# Price moved above upper band ?
elif self._values[-2] <= self.bb[-2].ub and self._values[-1] > self.bb[-1].ub:
self.logger.info(f" -> Price moved above the upper band -> SELL! 💲 💲 💲 ")
self.place_sell_order(self.api_client, self.logger, candle.base_close)

self.logger.info(f" Orders: ")
self.logger.info(f" > On-Chain BUY order: {self.buy_order_ref} ")
self.logger.info(f" > On-Chain SELL order: {self.sell_order_ref} ")
self.cancel_buy_orders()
if len(self.get_sell_orders()) > 0:
self.logger.info(" > Already placed SELL order. Nothing to do.")
else:
self.place_sell_order(self.api_client, self.logger, candle.base_low)

self.log_orders()

def log_orders(self):
own_orders = self.api_client.get_own_orders(self.market)

self.logger.info(" ON-CHAIN ORDERS:")

if (len(own_orders.asks) + len(own_orders.bids)) == 0:
self.logger.info(f" > No orders.")
return

for sell_order in own_orders.asks:
self.logger.info(f" > SELL: {sell_order.output_reference}")

for buy_order in own_orders.bids:
self.logger.info(f" > BUY: {buy_order.output_reference} ")

def execute(self, api_client : Api, CONFIG, logger):
current_time = datetime.now()

if self.last_execution_time is None:
logger.info("Executing for the first time")
logger.info("Executing for the first time -> initialize.")
candles = api_client.get_price_history(self.market, resolution="1m", sort="asc", limit=self.period*5)
for candle in candles[:-1]:
self.logger.info(f"--------------------------------------------------------------------------------")
self.process_candle(candle)
time.sleep(1)
logger.info(" > [OK] Initialized.")
logger.info("========================================================================")
self.initialized = True
self.last_candle=None
else:
Expand All @@ -204,16 +231,10 @@ def execute(self, api_client : Api, CONFIG, logger):
self.last_execution_time = current_time # Update last execution time
self.initialized = True

self.counter += 1
logger.info(f" > Counter: {self.counter}")

try:
get_market_price = api_client.get_market_price(self.market)
candle=get_market_price[0]
logger.info(f" > Base closing price: {candle.base_close}")
self.process_candle(candle)
except:
logger.error(f" > ⚠️ [FAILED] could not process candle ⚠️")
logger.exception(f" > Exception! ")

logger.info(f" > EXECUTION FINISHED.")
Loading