-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.py
298 lines (235 loc) · 13.1 KB
/
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
from fastapi import FastAPI
from binance.client import Client
import pandas as pd
import pandas_ta as ta
import time
import threading
import uvicorn
from datetime import timedelta,datetime
from supabase import create_client, Client as supabaseClient
from colorama import Fore
import os
from dotenv import load_dotenv
from discord_webhook import DiscordWebhook,DiscordEmbed
load_dotenv()
class Config:
# RSI clamp values
RSI_LOWER_THRESHOLD = 30
RSI_UPPER_THRESHOLD = 70
# Active token to trade
ACTIVE_TOKEN = 'BTCUSDT'
KLINE_INTERVAL = Client.KLINE_INTERVAL_1HOUR
#in seconds
REFRESH_INTERVAL = 3600
webhook = DiscordWebhook(url=os.getenv('WEBHOOK_URL'))
app = FastAPI()
apiKey = os.getenv('BINANCE_KEY')
apiSecret = os.getenv('BINANCE_SECRET')
supabaseURL : str = os.getenv('SUPABASE_URL')
supabaseKEY : str = os.getenv('SUPABASE_KEY')
supabase: supabaseClient = create_client(supabaseURL, supabaseKEY)
client = Client(apiKey, apiSecret,testnet=True)
client.API_URL = 'https://testnet.binance.vision/api'
openPositions = {}
client.timestamp_offset = client.get_server_time().get('serverTime') - time.time()*1000
cryptoSymbol = 'BTCUSDT'
botActive = True # Flag to control bot activation
lastCandleTimestamp = None
@app.post("/toggleBot")
async def toggleBot(status: bool):
global botActive
botActive = status
return {"message": f"Bot is now {'active' if botActive else 'inactive'}"}
@app.post("/checkStatus")
async def checkStatusEndpoint():
global botActive
return {"status": botActive}
@app.get("/calculateRsi")
async def calculateRsiEndpoint(symbol: str):
rsi = calculateRsi(symbol)
if rsi is not None:
return {"rsi": rsi}
else:
return {"error": "Error calculating RSI"}
@app.get("/calculateMacd")
async def calculateMacdEndpoint(symbol: str):
macd_info = calculateMacd(symbol)
if macd_info is not None:
return macd_info
else:
return {"error": "Error calculating MACD"}
def calculateMacd(symbol):
try:
klines = client.futures_klines(symbol=symbol, interval=Client.KLINE_INTERVAL_1HOUR)
close_prices = [float(kline[4]) for kline in klines]
# Create a DataFrame for the close prices
df = pd.DataFrame({'close': close_prices})
# Calculate MACD using pandas-ta
macd_info = ta.macd(df['close'], fast=12, slow=26, signal=9)
# Get the last MACD values
last_macd_values = macd_info.iloc[-1]
return {
"macd": last_macd_values['MACD_12_26_9'],
"signal_line": last_macd_values['MACDs_12_26_9'],
"histogram": last_macd_values['MACDh_12_26_9']
}
except Exception as e:
print(f"Error calculating MACD: {str(e)}")
return None
def calculateRsi(symbol):
try:
klines = client.futures_klines(symbol=symbol, interval=Client.KLINE_INTERVAL_1HOUR)
closePrices = [float(kline[4]) for kline in klines]
# Create a DataFrame for the close prices
df = pd.DataFrame({'close': closePrices})
# Calculate RSI using pandas-ta
rsi_column = ta.rsi(df['close'], length=14)
df['RSI_14'] = rsi_column
# Get the last RSI value
currentRsi = df['RSI_14'].iloc[-1]
print(closePrices[-1])
return currentRsi
except Exception as e:
print(f"Error calculating RSI: {str(e)}")
return None
def makeTrade(symbol, side, stop_loss_price=None):
try:
if stop_loss_price:
# Place a stop-loss limit order
client.futures_create_order(
symbol=symbol,
side='SELL' if side == Client.SIDE_BUY else 'BUY',
type=Client.ORDER_TYPE_STOP_MARKET,
quantity=0.01,
stopPrice=stop_loss_price
)
print(Fore.YELLOW+"[ORDER SUCCESS]: "+Fore.RESET+f"Successfully placed stop-loss order for {symbol} at {stop_loss_price}")
# Place the market order
response = client.futures_create_order(
symbol=symbol,
side=side,
type=Client.ORDER_TYPE_MARKET,
quantity=0.01
)
print(Fore.GREEN+"[ORDER SUCCESS]: "+Fore.RESET+f"Successfully placed {side} order for {symbol}")
return response['orderId']
except Exception as e:
print(f"Error placing {side} order: {str(e)}")
# def tradeBasedOnRsi(symbol):
# global openPositions
# print(openPositions)
# # Calculate the current RSI
# rsi = calculateRsi(symbol)
# print(Fore.BLUE+ Fore.BLUE+ "[TRADE INFO]:"+Fore.RESET+" Current RSI =", rsi)
# if rsi is not None:
# for position in openPositions:
# if position['symbol'] == symbol:
# if position['side'] == 'SELL' and rsi <= Config.RSI_LOWER_THRESHOLD:
# makeTrade(symbol, side=Client.SIDE_BUY)
# data, count = supabase.table('openPositions').delete().eq("symbol", symbol).execute()
# openPositions.remove(position)
# print(f"Closed short position for {symbol}")
# elif position['side'] == 'BUY' and rsi >= Config.RSI_UPPER_THRESHOLD:
# makeTrade(symbol, side=Client.SIDE_SELL)
# data, count = supabase.table('openPositions').delete().eq("symbol", symbol).execute()
# openPositions.remove(position)
# print(f"Closed long position for {symbol}")
# if symbol not in [position['symbol'] for position in openPositions]:
# if rsi <= Config.RSI_LOWER_THRESHOLD:
# makeTrade(symbol, side=Client.SIDE_SELL)
# data, count = supabase.table('openPositions').insert({'side': "SELL", 'rsiThreshold': rsi, "symbol": symbol}).execute()
# print(data)
# openPositions.append({'id': data[1][0]['id'], 'created_at': data[1][0]['created_at'], 'side': 'BUY', 'rsiThreshold': rsi, 'symbol': symbol})
# print(f"Opened short position for {symbol}")
# elif rsi >= Config.RSI_UPPER_THRESHOLD:
# makeTrade(symbol, side=Client.SIDE_BUY)
# data, count = supabase.table('openPositions').insert({'side': "BUY", 'rsiThreshold': rsi, "symbol": symbol}).execute()
# openPositions.append({'id': data[1][0]['id'], 'created_at': data[1][0]['created_at'], 'side': 'SELL', 'rsiThreshold': rsi, 'symbol': symbol})
# print(f"Opened long position for {symbol}")
def tradeBasedOnIndicators(symbol):
global openPositions
print(openPositions)
# Calculate the current RSI and MACD
rsi = calculateRsi(symbol)
macd_info = calculateMacd(symbol)
print(Fore.BLUE+ "[TRADE INFO]:"+Fore.RESET+" Current RSI =", rsi)
print(Fore.BLUE+ "[TRADE INFO]:"+Fore.RESET+" Current MACD =", macd_info['macd'])
if rsi is not None and macd_info is not None:
rsi_value = rsi
macd_value = macd_info['macd']
signal_line_value = macd_info['signal_line']
for position in openPositions:
if position['symbol'] == symbol:
if (
(position['side'] == 'SELL' and rsi_value <= Config.RSI_LOWER_THRESHOLD)
or (position['side'] == 'SELL' and macd_value > signal_line_value)
):
ClosingTradeId = makeTrade(symbol, side=Client.SIDE_BUY)
tradeInfo = client.futures_account_trades(symbol=symbol,orderId=ClosingTradeId)
calcProfit = (float(position['entryPrice']) - float(tradeInfo[0]['price'])) * 0.01
embed = DiscordEmbed(title=f"Trade Made!",color="fc2003")
embed.add_embed_field(f'Closed short position for {symbol}',f'Calculated profit/loss: {calcProfit}')
embed.set_timestamp()
webhook.add_embed(embed)
webhook.execute()
supabase.table('closedPositions').insert({'profit': calcProfit,'trade_id':position['id'],'direction': 'SHORT'}).execute()
supabase.table('openPositions').delete().eq('id',position['id']).execute()
openPositions.remove(position)
print(Fore.LIGHTRED_EX+ "[TRADE INFO]:"+Fore.RESET+ f"Closed short position for {symbol} with ID: {position['id']}")
elif ((position['side'] == 'BUY' and rsi_value >= Config.RSI_UPPER_THRESHOLD)
or (position['side'] == 'BUY' and macd_value < signal_line_value)):
ClosingTradeId = makeTrade(symbol, side=Client.SIDE_SELL)
tradeInfo = client.futures_account_trades(symbol=symbol,orderId=ClosingTradeId)
calcProfit = (float(position['entryPrice']) - float(tradeInfo[0]['price'])) * 0.01
embed = DiscordEmbed(title=f"Trade Made!",color="49fc03")
embed.add_embed_field(f'Closed long position for',f'Calculated profit/loss: {calcProfit}')
embed.set_timestamp()
webhook.add_embed(embed)
webhook.execute()
supabase.table('closedPositions').insert({'profit': calcProfit,'trade_id':position['id'],'direction': 'SHORT'}).execute()
supabase.table('openPositions').delete().eq('id', position['id']).execute()
openPositions.remove(position)
print(Fore.LIGHTRED_EX+ "[TRADE INFO]:"+Fore.RESET+ f"Closed long position for {symbol} with ID: {position['id']}")
if symbol not in [position['symbol'] for position in openPositions]:
if rsi_value <= Config.RSI_LOWER_THRESHOLD and macd_value < signal_line_value:
tradeID = makeTrade(symbol, side=Client.SIDE_SELL)
tradeInfo = client.futures_account_trades(symbol=symbol,orderId=tradeID)
data,count = supabase.table('openPositions').insert({'side': "SELL", 'rsiThreshold': rsi_value, 'macdThreshold': macd_value, "symbol": symbol, 'entryPrice':tradeInfo[0]['price']}).execute()
openPositions.append({'id': data[1][0]['id'], 'created_at': data[1][0]['created_at'], 'side': 'SELL', 'rsiThreshold': rsi_value, 'macdThreshold': macd_value, 'symbol': symbol,'entryPrice': tradeInfo[0]['price']})
print(Fore.LIGHTBLUE_EX+ "[TRADE INFO]:"+Fore.RESET+ f"Opened short position for {symbol}")
elif rsi_value >= Config.RSI_UPPER_THRESHOLD and macd_value > signal_line_value:
tradeID = makeTrade(symbol, side=Client.SIDE_BUY)
tradeInfo = client.futures_account_trades(symbol=symbol,orderId=tradeID)
data,count = supabase.table('openPositions').insert({'side': "BUY", 'rsiThreshold': rsi_value, 'macdThreshold': macd_value, "symbol": symbol,'entryPrice': tradeInfo[0]['price']}).execute()
openPositions.append({'id': data[1][0]['id'], 'created_at': data[1][0]['created_at'], 'side': 'BUY', 'rsiThreshold': rsi_value, 'macdThreshold': macd_value, 'symbol': symbol,'entryPrice': tradeInfo[0]['price']})
print(Fore.LIGHTBLUE_EX+ "[TRADE INFO]:"+Fore.RESET+ f"Opened long position for {symbol}")
def backgroundTask():
global lastCandleTimestamp
global cryptoSymbol
global openPositions
openPositions = supabase.table('openPositions').select('*').eq('symbol',cryptoSymbol).execute().data
print(openPositions)
while True:
if(not botActive):
time.sleep(100)
else:
# Replace with the cryptocurrency symbol you want to trade
print("[INFO]: Running bg task...")
# Fetch the latest candle's timestamp
klines = client.futures_klines(symbol=cryptoSymbol, interval=Config.KLINE_INTERVAL, limit=1)
latestCandleTimestamp = klines[0][0]
# Check if a new candle has appeared
print(lastCandleTimestamp,latestCandleTimestamp)
if lastCandleTimestamp is None or latestCandleTimestamp > lastCandleTimestamp:
lastCandleTimestamp = latestCandleTimestamp
print(Fore.BLUE+ "[TRADE INFO]:"+Fore.RESET+" Launching trading function")
#tradeBasedOnRsi(cryptoSymbol)
tradeBasedOnIndicators(cryptoSymbol)
# Wait for an hour before the next check
currentTime = (datetime.now() + timedelta(seconds=Config.REFRESH_INTERVAL)).strftime("%H:%M:%S")
print("[UPDATE INFO]: Next update - "+str(currentTime))
time.sleep(Config.REFRESH_INTERVAL)
if __name__ == '__main__':
thread = threading.Thread(target=backgroundTask)
thread.start()
uvicorn.run(app, host='0.0.0.0', port=5000)