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

Using 24h high to decide whether to buy in on a new announcement (pump detection) #59

Draft
wants to merge 1 commit into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
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
1 change: 1 addition & 0 deletions config.example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
# In %
SL: -3
TP: 2
24H: 2
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding % difference between 24h and current price for a buy decision

ENABLE_TSL: True
TSL: -4
TTP: 2
Expand Down
161 changes: 102 additions & 59 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ def main():
# store config deets
tp = config['TRADE_OPTIONS']['TP']
sl = config['TRADE_OPTIONS']['SL']
ath = config['TRADE_OPTIONS']['24H']
enable_tsl = config['TRADE_OPTIONS']['ENABLE_TSL']
tsl = config['TRADE_OPTIONS']['TSL']
ttp = config['TRADE_OPTIONS']['TTP']
Expand Down Expand Up @@ -88,23 +89,23 @@ def main():

logger.debug(f'Data for sell: {coin=} | {stored_price=} | {coin_tp=} | {coin_sl=} | {volume=} | {symbol=} ')

logger.info(f'get_last_price existing coin: {coin}')
last_price = get_last_price(symbol, pairing)
logger.info(f'get_coin_info existing coin: {coin}')
coin_info = get_coin_info(symbol, pairing)
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change from a single unit of information to all the info you can get on the coin. This is a majority of the changes, along with calling the info differently.


logger.info("Finished get_last_price")
logger.info(f'{last_price=}')
logger.info("Finished get_coin_info")
logger.info(f'{coin_info.last=}')
logger.info(f'{stored_price + (stored_price*sl /100)=}')

# update stop loss and take profit values if threshold is reached
if float(last_price) > stored_price + (
if float(coin_info.last) > stored_price + (
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here you call the last price with coin_info.last. You can use coin_info.xxx to pull different coin info.

See https://www.gate.io/docs/apiv4/en/#retrieve-ticker-information for the different options.

stored_price * coin_tp / 100) and enable_tsl:
# increase as absolute value for TP
new_tp = float(last_price) + (float(last_price) * ttp / 100)
new_tp = float(coin_info.last) + (float(coin_info.last) * ttp / 100)
# convert back into % difference from when the coin was bought
new_tp = float((new_tp - stored_price) / stored_price * 100)

# same deal as above, only applied to trailing SL
new_sl = float(last_price) + (float(last_price)*tsl / 100)
new_sl = float(coin_info.last) + (float(coin_info.last)*tsl / 100)
new_sl = float((new_sl - stored_price) / stored_price * 100)

# new values to be added to the json file
Expand All @@ -113,19 +114,19 @@ def main():
store_order('order.json', order)

logger.info(f'Updated TP: {round(new_tp, 3)} and SL:'
f' {round(new_sl, 3)}')
f' {round(new_sl, 3)}')

# close trade if tsl is reached or trail option is not enabled
elif float(last_price) < stored_price + (
stored_price * coin_sl / 100) or float(last_price) > stored_price + (
elif float(coin_info.last) < stored_price + (
stored_price * coin_sl / 100) or float(coin_info.last) > stored_price + (
stored_price * coin_tp / 100) and not enable_tsl:
try:
# sell for real if test mode is set to false
if not test_mode:
logger.info(f'starting sell place_order with :{symbol} | {pairing} | {float(volume)*float(last_price)} | side=sell {last_price}')
sell = place_order(symbol, pairing, float(volume)*float(last_price), 'sell', last_price)
logger.info(f'starting sell place_order with :{symbol} | {pairing} | {float(volume)*float(coin_info.last)} | side=sell {coin_info.last}')
sell = place_order(symbol, pairing, float(volume)*float(coin_info.last), 'sell', coin_info.last)
logger.info("Finish sell place_order")
logger.info(f'sold {coin} with {(float(last_price) - stored_price) / float(stored_price)*100}% PNL')
logger.info(f'sold {coin} with {(float(coin_info.last) - stored_price) / float(stored_price)*100}% PNL')

# remove order from json file
order.pop(coin)
Expand All @@ -147,12 +148,12 @@ def main():
else:
sold_coins[coin] = {
'symbol': coin,
'price': last_price,
'price': coin_info.last,
'volume': volume,
'time': datetime.timestamp(datetime.now()),
'profit': float(last_price) - stored_price,
'profit': float(coin_info.last) - stored_price,
'relative_profit_%': round((float(
last_price) - stored_price) / stored_price * 100, 3),
coin_info.last) - stored_price) / stored_price * 100, 3),
'id': 'test-order',
'text': 'test-order',
'create_time': datetime.timestamp(datetime.now()),
Expand All @@ -162,9 +163,8 @@ def main():
'type': 'limit',
'account': 'spot',
'side': 'sell',
'iceberg': '0',
'price': last_price}
logger.info('Sold coins:\r\n' + str(sold_coins[coin]))
'iceberg': '0'}
logger.info('Sold coins:' + str(sold_coins[coin]))
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know what the \r\n is for, and it bugged me, so I removed it, not important, take it or leave it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's for a newline. Nowadays, both \r and \n are liable to each add a newline, instead of both being required on windows. Generally just \n is good enough (if a newline is desired).


store_order('sold.json', sold_coins)

Expand All @@ -183,61 +183,104 @@ def main():
supported_currencies = get_all_currencies(single=True)
if supported_currencies:
if announcement_coin in supported_currencies:
logger.debug("Starting get_last_price")
price = get_last_price(announcement_coin, pairing)
logger.debug("Starting get_coin_info")
coin_info = get_coin_info(announcement_coin, pairing)

logger.debug('Coin price: ' + price)
logger.debug('Finished get_last_price')
logger.debug('24h High: ' + coin_info.high_24h)
logger.debug('Coin price: ' + coin_info.last)
logger.debug('Finished get_coin_info')

try:
# Run a test trade if true
if config['TRADE_OPTIONS']['TEST']:
order[announcement_coin] = {
'symbol': announcement_coin,
'price': price,
'volume': qty,
'time': datetime.timestamp(datetime.now()),
'tp': tp,
'sl': sl,
'id': 'test-order',
'text': 'test-order',
'create_time': datetime.timestamp(datetime.now()),
'update_time': datetime.timestamp(datetime.now()),
'currency_pair': f'{announcement_coin}_{pairing}',
'status': 'filled',
'type': 'limit',
'account': 'spot',
'side': 'buy',
'iceberg': '0'
}
logger.info('PLACING TEST ORDER')
logger.debug(order[announcement_coin])
if float(coin_info.high_24h) > float(coin_info.last) + (float(coin_info.last) * float(ath) / 100):
logger.info(f'24h High is {ath}% greater than price, coin not traded')
else:
order[announcement_coin] = {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here is where the decision to not start a buy order on a new announced coin based on the % difference between current price and 24h high is done, also added to the logger info below.

'symbol': announcement_coin,
'price': price,
'volume': qty,
'time': datetime.timestamp(datetime.now()),
'tp': tp,
'sl': sl,
'id': 'test-order',
'text': 'test-order',
'create_time': datetime.timestamp(datetime.now()),
'update_time': datetime.timestamp(datetime.now()),
'currency_pair': f'{announcement_coin}_{pairing}',
'status': 'filled',
'type': 'limit',
'account': 'spot',
'side': 'buy',
'iceberg': '0'
}
logger.info('PLACING TEST ORDER')
logger.debug(order[announcement_coin])
# place a live order if False
else:
logger.info(f'starting buy place_order with : {announcement_coin=} | {pairing=} | {qty=} | side = buy | {price=}')
order[announcement_coin] = place_order(announcement_coin, pairing, qty,'buy', price)
order[announcement_coin] = order[announcement_coin].__dict__
order[announcement_coin].pop("local_vars_configuration")
order[announcement_coin]['tp'] = tp
order[announcement_coin]['sl'] = sl
logger.info('Finished buy place_order')
if float(coin_info.high_24h) > float(coin_info.last) + (float(coin_info.last) * float(ath) / 100):
logger.info(f'24h High is {ath}% greater than price, coin not traded')
else:
logger.info(f'starting buy place_order with : {announcement_coin=} | {pairing=} | {qty=} | side = buy | {price=}')
order[announcement_coin] = place_order(announcement_coin, pairing, qty,'buy', price)
order[announcement_coin] = order[announcement_coin].__dict__
order[announcement_coin].pop("local_vars_configuration")
order[announcement_coin]['tp'] = tp
order[announcement_coin]['sl'] = sl
logger.info('Finished buy place_order')

except Exception as e:
logger.error(e)

else:
logger.info(f'Order created with {qty} on {announcement_coin}')
store_order('order.json', order)
if float(coin_info.high_24h) > float(coin_info.last) + (float(coin_info.last) * float(ath) / 100):
logger.info('No order status to report')
else:
if test_mode:
order_status = order[announcement_coin]['status']
else:
order_status = order[announcement_coin]['_status']

message = f'Order created on {announcement_coin} at a price of {price} each. {order_status=}'
logger.info(message)


if order_status == 'filled' or order_status == "closed":
if test_mode and float(order[announcement_coin]['_left']) > 0 and float(order[announcement_coin]['_amount']) > float(order[announcement_coin]['_left']):
# you can only sell what you have. Minus fees. Look for unfulfilled
newAmount = float(order[announcement_coin]['_amount']) - float(order[announcement_coin]['_left']) - float(order[announcement_coin]['_fee'])
order[announcement_coin]['volume'] = newAmount
else:
store_order('order_fulfilled.json', order)

# you can only sell what you have. Minus fees. Look for unfulfilled
newAmount = float(order[announcement_coin]['_amount']) - float(order[announcement_coin]['_left']) - float(order[announcement_coin]['_fee'])
order[announcement_coin]['_amount'] = newAmount

store_order('order.json', order)


elif order_status == 'open' or order_status == 'cancelled':
if not test_mode and order_status == 'open':
# cancel orders and try again in the next iteration
cancel_open_order(order[announcement_coin]['_id'], announcement_coin, pairing)
logger.info(f"Cancelled order {order[announcement_coin]['_id']} . Waiting for status of 'filled/closed' for {announcement_coin}")

order.clear() # reset for next iteration

else:
logger.warning(f'{announcement_coin=} is not supported on gate io')
if os.path.isfile('new_listing.json'):
os.remove("new_listing.json")
logger.debug('Removed new_listing.json due to coin not being '
'listed on gate io')
if announcement_coin:
logger.warning(f'{announcement_coin=} is not supported on gate io')
if os.path.isfile('new_listing.json'):
os.remove("new_listing.json")
logger.debug('Removed new_listing.json due to coin not being '
'listed on gate io')
else:
logger.error('supported_currencies is not initialized')
else:
logger.info( 'No coins announced, or coin has already been bought/sold. Checking more frequently in case TP and SL need updating')

logger.info( 'No coins announced, or coin has already been bought/sold.')


time.sleep(3)
# except Exception as e:
Expand All @@ -251,4 +294,4 @@ def main():

if __name__ == '__main__':
logger.info('working...')
main()
main()
9 changes: 5 additions & 4 deletions trade_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,13 @@
spot_api = SpotApi(ApiClient(client))


def get_last_price(base,quote):
def get_coin_info(base,quote):
"""
Args:
'DOT', 'USDT'
"""
tickers = spot_api.list_tickers(currency_pair=f'{base}_{quote}')
assert len(tickers) == 1
return tickers[0].last
return tickers[0]

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Allowing the array to contain all the info, not just .last.


def get_min_amount(base,quote):
Expand All @@ -36,8 +35,10 @@ def place_order(base,quote, amount, side, last_price):
'DOT', 'USDT', 50, 'buy', 400
"""
try:
order = Order(amount=str(float(amount)/float(last_price)), price=last_price, side=side, currency_pair=f'{base}_{quote}')
order = Order(amount=str(float(amount)/float(last_price)), price=last_price, side=side, currency_pair=f'{base}_{quote}', time_in_force='ioc')
order = spot_api.create_order(order)
t = order
logger.info(f"PLACE ORDER: {t.side} | {t.id} | {t.account} | {t.type} | {t.currency_pair} | {t.status} | amount={t.amount} | price={t.price} | left={t.left} | filled_total={t.filled_total} | fill_price={t.fill_price}")
except Exception as e:
logger.error(e)
raise
Expand Down