├── README.md
├── cBot_perp_ftx.py
├── exemples
└── aligatorPerp
│ ├── Complete_Future_Backtest_Aligator_SL.ipynb
│ ├── aligatorPerpLive.py
│ └── cBot_perp_ftx.py
└── requirements.txt
/README.md:
--------------------------------------------------------------------------------
1 | # cBot
2 |
3 | /!\ This code may not work properly if you don't have the same version of package especially the ccxt package /!\
4 | To install a working version of package run the command below :
5 | ```
6 | pip install -r requirements.txt
7 | ```
8 |
--------------------------------------------------------------------------------
/cBot_perp_ftx.py:
--------------------------------------------------------------------------------
1 | import ccxt
2 | import pandas as pd
3 | import json
4 | import time
5 |
6 | class cBot_perp_ftx():
7 | def __init__(self, apiKey=None, secret=None, subAccountName=None):
8 | ftxAuthObject = {
9 | "apiKey": apiKey,
10 | "secret": secret,
11 | 'headers': {
12 | 'FTX-SUBACCOUNT': subAccountName
13 | }
14 | }
15 | if ftxAuthObject['secret'] == None:
16 | self._auth = False
17 | self._session = ccxt.ftx()
18 | else:
19 | self._auth = True
20 | self._session = ccxt.ftx(ftxAuthObject)
21 | self._session.load_markets()
22 |
23 | def authentication_required(fn):
24 | """Annotation for methods that require auth."""
25 | def wrapped(self, *args, **kwargs):
26 | if not self._auth:
27 | print("You must be authenticated to use this method", fn)
28 | exit()
29 | else:
30 | return fn(self, *args, **kwargs)
31 | return wrapped
32 |
33 | def get_historical_since(self, symbol, timeframe, startDate):
34 | try:
35 | tempData = self._session.fetch_ohlcv(symbol, timeframe, int(
36 | time.time()*1000)-1209600000, limit=1000)
37 | dtemp = pd.DataFrame(tempData)
38 | timeInter = int(dtemp.iloc[-1][0] - dtemp.iloc[-2][0])
39 | except:
40 | return None
41 |
42 | finished = False
43 | start = False
44 | allDf = []
45 | startDate = self._session.parse8601(startDate)
46 | while(start == False):
47 | try:
48 | tempData = self._session.fetch_ohlcv(
49 | symbol, timeframe, startDate, limit=1000)
50 | dtemp = pd.DataFrame(tempData)
51 | timeInter = int(dtemp.iloc[-1][0] - dtemp.iloc[-2][0])
52 | nextTime = int(dtemp.iloc[-1][0] + timeInter)
53 | allDf.append(dtemp)
54 | start = True
55 | except:
56 | startDate = startDate + 1209600000*2
57 |
58 | if dtemp.shape[0] < 1:
59 | finished = True
60 | while(finished == False):
61 | try:
62 | tempData = self._session.fetch_ohlcv(
63 | symbol, timeframe, nextTime, limit=1000)
64 | dtemp = pd.DataFrame(tempData)
65 | nextTime = int(dtemp.iloc[-1][0] + timeInter)
66 | allDf.append(dtemp)
67 | if dtemp.shape[0] < 1:
68 | finished = True
69 | except:
70 | finished = True
71 | result = pd.concat(allDf, ignore_index=True, sort=False)
72 | result = result.rename(
73 | columns={0: 'timestamp', 1: 'open', 2: 'high', 3: 'low', 4: 'close', 5: 'volume'})
74 | result = result.set_index(result['timestamp'])
75 | result.index = pd.to_datetime(result.index, unit='ms')
76 | del result['timestamp']
77 | return result
78 |
79 | def get_last_historical(self, symbol, timeframe, limit):
80 | result = pd.DataFrame(data=self._session.fetch_ohlcv(
81 | symbol, timeframe, None, limit=limit))
82 | result = result.rename(
83 | columns={0: 'timestamp', 1: 'open', 2: 'high', 3: 'low', 4: 'close', 5: 'volume'})
84 | result = result.set_index(result['timestamp'])
85 | result.index = pd.to_datetime(result.index, unit='ms')
86 | del result['timestamp']
87 | return result
88 |
89 | def get_min_order_amount(self, symbol):
90 | return self._session.markets_by_id[symbol]['limits']['amount']['min']
91 |
92 | def convert_amount_to_precision(self, symbol, amount):
93 | return self._session.amount_to_precision(symbol, amount)
94 |
95 | def convert_price_to_precision(self, symbol, price):
96 | return self._session.price_to_precision(symbol, price)
97 |
98 | @authentication_required
99 | def get_all_balance(self):
100 | try:
101 | allBalance = self._session.fetchBalance()
102 | except BaseException as err:
103 | raise TypeError("An error occured in get_all_balance", err)
104 | return allBalance['total']
105 |
106 | @authentication_required
107 | def get_balance_of_one_coin(self, coin):
108 | try:
109 | allBalance = self._session.fetchBalance()
110 | except BaseException as err:
111 | raise TypeError("An error occured in get_balance_of_one_coin", err)
112 | try:
113 | return allBalance['total'][coin]
114 | except:
115 | return 0
116 |
117 | @authentication_required
118 | def place_market_order(self, symbol, side, amount, leverage=1):
119 | try:
120 | return self._session.createOrder(
121 | symbol,
122 | 'market',
123 | side,
124 | self.convert_amount_to_precision(symbol, amount * leverage),
125 | None
126 | )
127 | except BaseException as err:
128 | raise TypeError("An error occured in place_market_order", err)
129 |
130 | @authentication_required
131 | def place_reduce_market_order(self, symbol, side, amount, leverage=1):
132 | params = {
133 | 'reduceOnly':True
134 | }
135 | try:
136 | return self._session.createOrder(
137 | symbol,
138 | 'market',
139 | side,
140 | self.convert_amount_to_precision(symbol, amount * leverage),
141 | None,
142 | params
143 | )
144 | except BaseException as err:
145 | raise TypeError("An error occured in place_reduce_market_order", err)
146 |
147 | @authentication_required
148 | def place_limit_order(self, symbol, side, amount, price, leverage=1):
149 | try:
150 | return self._session.createOrder(
151 | symbol,
152 | 'limit',
153 | side,
154 | self.convert_amount_to_precision(symbol, amount * leverage),
155 | self.convert_price_to_precision(symbol, price)
156 | )
157 | except BaseException as err:
158 | raise TypeError("An error occured in place_limit_order", err)
159 |
160 | @authentication_required
161 | def place_reduce_limit_order(self, symbol, side, amount, price, leverage=1):
162 | params = {
163 | 'reduceOnly':True
164 | }
165 | try:
166 | return self._session.createOrder(
167 | symbol,
168 | 'limit',
169 | side,
170 | self.convert_amount_to_precision(symbol, amount * leverage),
171 | self.convert_price_to_precision(symbol, price),
172 | params
173 | )
174 | except BaseException as err:
175 | raise TypeError("An error occured in place_reduce_limit_order", err)
176 |
177 | @authentication_required
178 | def place_market_stop_loss(self, symbol, side, amount, price, leverage=1):
179 | params = {
180 | 'stopPrice': self.convert_price_to_precision(symbol, price), # your stop price
181 | 'reduceOnly':True
182 | }
183 | try:
184 | return self._session.createOrder(
185 | symbol,
186 | 'stop',
187 | side,
188 | self.convert_amount_to_precision(symbol, amount * leverage),
189 | None,
190 | params
191 | )
192 | except BaseException as err:
193 | raise TypeError("An error occured in place_market_stop_loss", err)
194 |
195 | @authentication_required
196 | def place_market_take_profit(self, symbol, side, amount, price, leverage=1):
197 | params = {
198 | 'stopPrice': self.convert_price_to_precision(symbol, price), # your stop price
199 | 'reduceOnly':True
200 | }
201 | try:
202 | return self._session.createOrder(
203 | symbol,
204 | 'takeProfit',
205 | side,
206 | self.convert_amount_to_precision(symbol, amount * leverage),
207 | None,
208 | params
209 | )
210 | except BaseException as err:
211 | raise TypeError("An error occured in place_market_take_profit", err)
212 |
213 |
214 | @authentication_required
215 | def cancel_all_open_order(self, symbol):
216 | try:
217 | return self._session.cancel_all_orders(symbol)
218 | except BaseException as err:
219 | raise TypeError("An error occured in cancel_all_open_order", err)
220 |
221 | @authentication_required
222 | def cancel_order_by_id(self, id):
223 | try:
224 | return self._session.cancel_order(id)
225 | except BaseException as err:
226 | raise TypeError("An error occured in cancel_order_by_id", err)
227 |
228 | @authentication_required
229 | def get_open_order(self, symbol=None):
230 | try:
231 | return self._session.fetchOpenOrders(symbol, None, None)
232 | except BaseException as err:
233 | raise TypeError("An error occured in get_open_order", err)
234 |
235 | @authentication_required
236 | def get_open_conditionnal_order(self, symbol=None):
237 | params = {
238 | 'type':'stop'
239 | }
240 | try:
241 | return self._session.fetchOpenOrders(symbol,None,None,params)
242 | except BaseException as err:
243 | raise TypeError("An error occured in get_open_conditionnal_order", err)
244 |
245 | @authentication_required
246 | def get_my_trades(self, symbol=None, since=None, limit=1):
247 | try:
248 | return self._session.fetch_my_trades(symbol, since, limit)
249 | except BaseException as err:
250 | raise TypeError("An error occured in get_my_trades", err)
251 |
252 | @authentication_required
253 | def get_open_position(self,symbol=None):
254 | try:
255 | positions = self._session.fetchPositions(symbol)
256 | truePositions = []
257 | for position in positions:
258 | if float(position['contracts']) > 0:
259 | truePositions.append(position)
260 | return truePositions
261 | except BaseException as err:
262 | raise TypeError("An error occured in get_open_position", err)
263 |
264 | @authentication_required
265 | def close_all_open_position(self,symbol=None):
266 | try:
267 | positions = self._session.fetchPositions(symbol)
268 | for position in positions:
269 | if position['side'] == 'long' and position['contracts'] > 0:
270 | self.place_reduce_market_order(position['symbol'], 'sell', position['contracts'])
271 | elif position['side'] == 'short' and position['contracts'] > 0:
272 | self.place_reduce_market_order(position['symbol'], 'buy', position['contracts'])
273 | return 'Close all positions done'
274 | except BaseException as err:
275 | raise TypeError("An error occured in close_all_open_position", err)
276 |
--------------------------------------------------------------------------------
/exemples/aligatorPerp/Complete_Future_Backtest_Aligator_SL.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "nbformat": 4,
3 | "nbformat_minor": 0,
4 | "metadata": {
5 | "orig_nbformat": 4,
6 | "language_info": {
7 | "name": "python",
8 | "version": "3.9.0",
9 | "mimetype": "text/x-python",
10 | "codemirror_mode": {
11 | "name": "ipython",
12 | "version": 3
13 | },
14 | "pygments_lexer": "ipython3",
15 | "nbconvert_exporter": "python",
16 | "file_extension": ".py"
17 | },
18 | "kernelspec": {
19 | "name": "python3",
20 | "display_name": "Python 3.9.0 64-bit"
21 | },
22 | "interpreter": {
23 | "hash": "4fbdd50cab4ce783176366d9e9b9a5fc0af9b2909d020e44f7991b413f247d95"
24 | },
25 | "colab": {
26 | "name": "Complete Future Backtest Aligator SL.ipynb",
27 | "provenance": [],
28 | "collapsed_sections": []
29 | }
30 | },
31 | "cells": [
32 | {
33 | "cell_type": "code",
34 | "metadata": {
35 | "id": "4AYk2bKelB6x"
36 | },
37 | "source": [
38 | "pip install python-binance & pip install ta"
39 | ],
40 | "execution_count": null,
41 | "outputs": []
42 | },
43 | {
44 | "cell_type": "markdown",
45 | "metadata": {
46 | "id": "vL2r-6X3k-PU"
47 | },
48 | "source": [
49 | "
Load yours data
"
50 | ]
51 | },
52 | {
53 | "cell_type": "code",
54 | "metadata": {
55 | "colab": {
56 | "base_uri": "https://localhost:8080/"
57 | },
58 | "id": "Wf_ab2Fjk-PY",
59 | "outputId": "30f9a8a0-3194-455e-c2b6-344c6466dd42"
60 | },
61 | "source": [
62 | "# -- Import --\n",
63 | "import pandas as pd\n",
64 | "from binance.client import Client\n",
65 | "import ta\n",
66 | "import matplotlib.pyplot as plt\n",
67 | "import numpy as np\n",
68 | "\n",
69 | "# -- Define Binance Client --\n",
70 | "client = Client()\n",
71 | "\n",
72 | "# -- You can change the crypto pair ,the start date and the time interval below --\n",
73 | "pairName = \"ETHUSDT\"\n",
74 | "startDate = \"01 january 2017\"\n",
75 | "timeInterval = Client.KLINE_INTERVAL_1HOUR\n",
76 | "\n",
77 | "# -- Load all price data from binance API --\n",
78 | "klinesT = client.get_historical_klines(pairName, timeInterval, startDate)\n",
79 | "\n",
80 | "# -- Define your dataset --\n",
81 | "df = pd.DataFrame(klinesT, columns = ['timestamp', 'open', 'high', 'low', 'close', 'volume', 'close_time', 'quote_av', 'trades', 'tb_base_av', 'tb_quote_av', 'ignore' ])\n",
82 | "df['close'] = pd.to_numeric(df['close'])\n",
83 | "df['high'] = pd.to_numeric(df['high'])\n",
84 | "df['low'] = pd.to_numeric(df['low'])\n",
85 | "df['open'] = pd.to_numeric(df['open'])\n",
86 | "\n",
87 | "# -- Set the date to index --\n",
88 | "df = df.set_index(df['timestamp'])\n",
89 | "df.index = pd.to_datetime(df.index, unit='ms')\n",
90 | "del df['timestamp']\n",
91 | "\n",
92 | "print(\"Data loaded 100%\")\n",
93 | "\n",
94 | "# -- Uncomment the line below if you want to check your price dataset --\n",
95 | "# df"
96 | ],
97 | "execution_count": null,
98 | "outputs": [
99 | {
100 | "output_type": "stream",
101 | "name": "stdout",
102 | "text": [
103 | "Data loaded 100%\n"
104 | ]
105 | }
106 | ]
107 | },
108 | {
109 | "cell_type": "markdown",
110 | "metadata": {
111 | "id": "R6_21o53k-Pb"
112 | },
113 | "source": [
114 | "Define your indicators
"
115 | ]
116 | },
117 | {
118 | "cell_type": "code",
119 | "metadata": {
120 | "id": "dSZNGfBgk-Pc"
121 | },
122 | "source": [
123 | "# -- Drop all columns we do not need --\n",
124 | "df.drop(df.columns.difference(['open','high','low','close','volume']), 1, inplace=True)\n",
125 | "\n",
126 | "# -- Indicators, you can edit every value --\n",
127 | "# -- Exponential Moving Average --\n",
128 | "df['EMA1']= ta.trend.ema_indicator(close=df['close'], window=7)\n",
129 | "df['EMA2']= ta.trend.ema_indicator(close=df['close'], window=30)\n",
130 | "df['EMA3']= ta.trend.ema_indicator(close=df['close'], window=50)\n",
131 | "df['EMA4']= ta.trend.ema_indicator(close=df['close'], window=100)\n",
132 | "df['EMA5']= ta.trend.ema_indicator(close=df['close'], window=121)\n",
133 | "df['EMA6']= ta.trend.ema_indicator(close=df['close'], window=200)\n",
134 | "# -- Stochasitc RSI --\n",
135 | "df['STOCH_RSI'] = ta.momentum.stochrsi(close=df['close'], window=14, smooth1=3, smooth2=3)\n",
136 | "\n",
137 | "print(\"Indicators loaded 100%\")\n",
138 | "\n",
139 | "# -- Uncomment the line below if you want to check your dataset with indicators --\n",
140 | "df"
141 | ],
142 | "execution_count": null,
143 | "outputs": []
144 | },
145 | {
146 | "cell_type": "markdown",
147 | "metadata": {
148 | "id": "pUt8RHMok-Pc"
149 | },
150 | "source": [
151 | "RUN the BackTest
"
152 | ]
153 | },
154 | {
155 | "cell_type": "code",
156 | "metadata": {
157 | "id": "qA6zFOOok-Pd"
158 | },
159 | "source": [
160 | "dfTest = df.copy()\n",
161 | "\n",
162 | "# -- If you want to run your BackTest on a specific period, uncomment the line below --\n",
163 | "# dfTest = df['2021-03-01':'2021-09-01']\n",
164 | "\n",
165 | "# -- Definition of dt, that will be the dataset to do your trades analyses --\n",
166 | "dt = None\n",
167 | "dt = pd.DataFrame(columns=['date', 'position', 'reason',\n",
168 | " 'price', 'frais', 'wallet', 'drawBack'])\n",
169 | "\n",
170 | "# -- You can change variables below --\n",
171 | "leverage = 2\n",
172 | "wallet = 1000\n",
173 | "makerFee = 0.0002\n",
174 | "takerFee = 0.0007\n",
175 | "\n",
176 | "# -- Do not touch these values --\n",
177 | "initalWallet = wallet\n",
178 | "lastAth = wallet\n",
179 | "previousRow = dfTest.iloc[0]\n",
180 | "stopLoss = 0\n",
181 | "takeProfit = 500000\n",
182 | "orderInProgress = ''\n",
183 | "longIniPrice = 0\n",
184 | "shortIniPrice = 0\n",
185 | "longLiquidationPrice = 500000\n",
186 | "shortLiquidationPrice = 0\n",
187 | "\n",
188 | "# -- Condition to open Market LONG --\n",
189 | "def openLongCondition(row, previousRow):\n",
190 | " if (row['EMA1'] > row['EMA2'] \n",
191 | " and row['EMA2'] > row['EMA3'] \n",
192 | " and row['EMA3'] > row['EMA4'] \n",
193 | " and row['EMA4'] > row['EMA5'] \n",
194 | " and row['EMA5'] > row['EMA6'] \n",
195 | " and row['STOCH_RSI'] < 0.82):\n",
196 | " return True\n",
197 | " else:\n",
198 | " return False\n",
199 | "\n",
200 | "# -- Condition to close Market LONG --\n",
201 | "def closeLongCondition(row, previousRow):\n",
202 | " if (row['EMA6'] > row['EMA1']):\n",
203 | " return True\n",
204 | " else:\n",
205 | " return False\n",
206 | "\n",
207 | "# -- Condition to open Market SHORT --\n",
208 | "def openShortCondition(row, previousRow):\n",
209 | " if ( row['EMA6'] > row['EMA5'] \n",
210 | " and row['EMA5'] > row['EMA4'] \n",
211 | " and row['EMA4'] > row['EMA3'] \n",
212 | " and row['EMA3'] > row['EMA2'] \n",
213 | " and row['EMA2'] > row['EMA1'] \n",
214 | " and row['STOCH_RSI'] > 0.2 ):\n",
215 | " return True\n",
216 | " else:\n",
217 | " return False\n",
218 | "\n",
219 | "# -- Condition to close Market SHORT --\n",
220 | "def closeShortCondition(row, previousRow):\n",
221 | " if (row['EMA1'] > row['EMA6']):\n",
222 | " return True\n",
223 | " else:\n",
224 | " return False\n",
225 | "\n",
226 | "# -- Iteration on all your price dataset (df) --\n",
227 | "for index, row in dfTest.iterrows():\n",
228 | "\n",
229 | " # -- If there is an order in progress --\n",
230 | " if orderInProgress != '':\n",
231 | " # -- Check if there is a LONG order in progress --\n",
232 | " if orderInProgress == 'LONG':\n",
233 | " # -- Check Liquidation --\n",
234 | " if row['low'] < longLiquidationPrice:\n",
235 | " print('/!\\ YOUR LONG HAVE BEEN LIQUIDATED the',index)\n",
236 | " break\n",
237 | " \n",
238 | " # -- Check Stop Loss --\n",
239 | " elif row['low'] < stopLoss:\n",
240 | " orderInProgress = ''\n",
241 | " closePrice = stopLoss\n",
242 | " closePriceWithFee = closePrice - takerFee * closePrice\n",
243 | " pr_change = (closePriceWithFee - longIniPrice) / longIniPrice\n",
244 | " wallet = wallet + wallet*pr_change*leverage\n",
245 | "\n",
246 | " # -- You can uncomment the line below if you want to see logs --\n",
247 | " # print('Close LONG at',closePrice,\"the\", index, '| wallet :', wallet,\n",
248 | " # '| result :', pr_change*100*leverage)\n",
249 | "\n",
250 | "\n",
251 | " # -- Check if your wallet hit a new ATH to know the drawBack --\n",
252 | " if wallet > lastAth:\n",
253 | " lastAth = wallet\n",
254 | " \n",
255 | " # -- Add the trade to DT to analyse it later --\n",
256 | " myrow = {'date': index, 'position': \"LONG\", 'reason': 'Stop Loss Long', 'price': closePrice,\n",
257 | " 'frais': takerFee * wallet * leverage, 'wallet': wallet, 'drawBack': (wallet-lastAth)/lastAth}\n",
258 | " dt = dt.append(myrow, ignore_index=True)\n",
259 | "\n",
260 | " # -- Check If you have to close the LONG --\n",
261 | " elif closeLongCondition(row, previousRow) == True:\n",
262 | " orderInProgress = ''\n",
263 | " closePrice = row['close']\n",
264 | " closePriceWithFee = row['close'] - takerFee * row['close']\n",
265 | " pr_change = (closePriceWithFee - longIniPrice) / longIniPrice\n",
266 | " wallet = wallet + wallet*pr_change*leverage\n",
267 | "\n",
268 | " # -- You can uncomment the line below if you want to see logs --\n",
269 | " # print('Close LONG at',closePrice,\"the\", index, '| wallet :', wallet,\n",
270 | " # '| result :', pr_change*100*leverage)\n",
271 | "\n",
272 | "\n",
273 | " # -- Check if your wallet hit a new ATH to know the drawBack --\n",
274 | " if wallet > lastAth:\n",
275 | " lastAth = wallet\n",
276 | " \n",
277 | " # -- Add the trade to DT to analyse it later --\n",
278 | " myrow = {'date': index, 'position': \"LONG\", 'reason': 'Close Long Market', 'price': closePrice,\n",
279 | " 'frais': takerFee * wallet * leverage, 'wallet': wallet, 'drawBack': (wallet-lastAth)/lastAth}\n",
280 | " dt = dt.append(myrow, ignore_index=True)\n",
281 | "\n",
282 | " # -- Check if there is a SHORT order in progress --\n",
283 | " elif orderInProgress == 'SHORT':\n",
284 | " # -- Check Liquidation --\n",
285 | " if row['high'] > shortLiquidationPrice:\n",
286 | " print('/!\\ YOUR SHORT HAVE BEEN LIQUIDATED the',index)\n",
287 | " break\n",
288 | "\n",
289 | " # -- Check stop loss --\n",
290 | " elif row['high'] > stopLoss:\n",
291 | " orderInProgress = ''\n",
292 | " closePrice = stopLoss\n",
293 | " closePriceWithFee = closePrice + takerFee * closePrice\n",
294 | " pr_change = -(closePriceWithFee - shortIniPrice) / shortIniPrice\n",
295 | " wallet = wallet + wallet*pr_change*leverage\n",
296 | "\n",
297 | " # -- You can uncomment the line below if you want to see logs --\n",
298 | " # print('Close SHORT at',closePrice,\"the\", index, '| wallet :', wallet,\n",
299 | " # '| result :', pr_change*100*leverage)\n",
300 | "\n",
301 | " # -- Check if your wallet hit a new ATH to know the drawBack --\n",
302 | " if wallet > lastAth:\n",
303 | " lastAth = wallet\n",
304 | "\n",
305 | " # -- Add the trade to DT to analyse it later --\n",
306 | " myrow = {'date': index, 'position': \"SHORT\", 'reason': 'Stop Loss Short', 'price': closePrice,\n",
307 | " 'frais': takerFee * wallet * leverage, 'wallet': wallet, 'drawBack': (wallet-lastAth)/lastAth}\n",
308 | " dt = dt.append(myrow, ignore_index=True)\n",
309 | "\n",
310 | "\n",
311 | " # -- Check If you have to close the SHORT --\n",
312 | " elif closeShortCondition(row, previousRow) == True:\n",
313 | " orderInProgress = ''\n",
314 | " closePrice = row['close']\n",
315 | " closePriceWithFee = row['close'] + takerFee * row['close']\n",
316 | " pr_change = -(closePriceWithFee - shortIniPrice) / shortIniPrice\n",
317 | " wallet = wallet + wallet*pr_change*leverage\n",
318 | "\n",
319 | " # -- You can uncomment the line below if you want to see logs --\n",
320 | " # print('Close SHORT at',closePrice,\"the\", index, '| wallet :', wallet,\n",
321 | " # '| result :', pr_change*100*leverage)\n",
322 | "\n",
323 | " # -- Check if your wallet hit a new ATH to know the drawBack --\n",
324 | " if wallet > lastAth:\n",
325 | " lastAth = wallet\n",
326 | "\n",
327 | " # -- Add the trade to DT to analyse it later --\n",
328 | " myrow = {'date': index, 'position': \"SHORT\", 'reason': 'Close Short Market', 'price': closePrice,\n",
329 | " 'frais': takerFee * wallet * leverage, 'wallet': wallet, 'drawBack': (wallet-lastAth)/lastAth}\n",
330 | " dt = dt.append(myrow, ignore_index=True)\n",
331 | "\n",
332 | " # -- If there is NO order in progress --\n",
333 | " if orderInProgress == '':\n",
334 | " # -- Check If you have to open a LONG --\n",
335 | " if openLongCondition(row, previousRow) == True:\n",
336 | " orderInProgress = 'LONG'\n",
337 | " closePrice = row['close']\n",
338 | " longIniPrice = row['close'] + takerFee * row['close']\n",
339 | " tokenAmount = (wallet * leverage) / row['close']\n",
340 | " longLiquidationPrice = longIniPrice - (wallet/tokenAmount)\n",
341 | " stopLoss = closePrice - 0.03 * closePrice\n",
342 | " # -- You can uncomment the line below if you want to see logs --\n",
343 | " # print('Open LONG at', closePrice, '$ the', index, '| Liquidation price :', longLiquidationPrice)\n",
344 | "\n",
345 | " # -- Add the trade to DT to analyse it later --\n",
346 | " myrow = {'date': index, 'position': \"Open Long\", 'reason': 'Open Long Market', 'price': closePrice,\n",
347 | " 'frais': takerFee * wallet * leverage, 'wallet': wallet, 'drawBack': (wallet-lastAth)/lastAth}\n",
348 | " dt = dt.append(myrow, ignore_index=True)\n",
349 | " \n",
350 | " # -- Check If you have to open a SHORT --\n",
351 | " if openShortCondition(row, previousRow) == True:\n",
352 | " orderInProgress = 'SHORT'\n",
353 | " closePrice = row['close']\n",
354 | " shortIniPrice = row['close'] - takerFee * row['close']\n",
355 | " tokenAmount = (wallet * leverage) / row['close']\n",
356 | " shortLiquidationPrice = shortIniPrice + (wallet/tokenAmount)\n",
357 | " stopLoss = closePrice + 0.03 * closePrice\n",
358 | " # -- You can uncomment the line below if you want to see logs --\n",
359 | " # print('Open SHORT', closePrice, '$ the', index, '| Liquidation price :', shortLiquidationPrice)\n",
360 | "\n",
361 | " # -- Add the trade to DT to analyse it later --\n",
362 | " myrow = {'date': index, 'position': \"Open Short\", 'reason': 'Open Short Market', 'price': closePrice,\n",
363 | " 'frais': takerFee * wallet * leverage, 'wallet': wallet, 'drawBack': (wallet-lastAth)/lastAth}\n",
364 | " dt = dt.append(myrow, ignore_index=True)\n",
365 | "\n",
366 | "\n",
367 | "# -- BackTest Analyses --\n",
368 | "dt = dt.set_index(dt['date'])\n",
369 | "dt.index = pd.to_datetime(dt.index)\n",
370 | "dt['resultat%'] = dt['wallet'].pct_change()*100\n",
371 | "\n",
372 | "dt['tradeIs'] = ''\n",
373 | "dt.loc[dt['resultat%'] > 0, 'tradeIs'] = 'Good'\n",
374 | "dt.loc[dt['resultat%'] < 0, 'tradeIs'] = 'Bad'\n",
375 | "\n",
376 | "iniClose = dfTest.iloc[0]['close']\n",
377 | "lastClose = dfTest.iloc[len(dfTest)-1]['close']\n",
378 | "holdPercentage = ((lastClose - iniClose)/iniClose)\n",
379 | "holdWallet = holdPercentage * leverage * initalWallet\n",
380 | "algoPercentage = ((wallet - initalWallet)/initalWallet)\n",
381 | "vsHoldPercentage = ((wallet - holdWallet)/holdWallet) * 100\n",
382 | "\n",
383 | "try:\n",
384 | " tradesPerformance = round(dt.loc[(dt['tradeIs'] == 'Good') | (dt['tradeIs'] == 'Bad'), 'resultat%'].sum()\n",
385 | " / dt.loc[(dt['tradeIs'] == 'Good') | (dt['tradeIs'] == 'Bad'), 'resultat%'].count(), 2)\n",
386 | "except:\n",
387 | " tradesPerformance = 0\n",
388 | " print(\"/!\\ There is no Good or Bad Trades in your BackTest, maybe a problem...\")\n",
389 | "\n",
390 | "try:\n",
391 | " TotalGoodTrades = dt.groupby('tradeIs')['date'].nunique()['Good']\n",
392 | " AveragePercentagePositivTrades = round(dt.loc[dt['tradeIs'] == 'Good', 'resultat%'].sum()\n",
393 | " / dt.loc[dt['tradeIs'] == 'Good', 'resultat%'].count(), 2)\n",
394 | " idbest = dt.loc[dt['tradeIs'] == 'Good', 'resultat%'].idxmax()\n",
395 | " bestTrade = str(\n",
396 | " round(dt.loc[dt['tradeIs'] == 'Good', 'resultat%'].max(), 2))\n",
397 | "except:\n",
398 | " TotalGoodTrades = 0\n",
399 | " AveragePercentagePositivTrades = 0\n",
400 | " idbest = ''\n",
401 | " bestTrade = 0\n",
402 | " print(\"/!\\ There is no Good Trades in your BackTest, maybe a problem...\")\n",
403 | "\n",
404 | "try:\n",
405 | " TotalBadTrades = dt.groupby('tradeIs')['date'].nunique()['Bad']\n",
406 | " AveragePercentageNegativTrades = round(dt.loc[dt['tradeIs'] == 'Bad', 'resultat%'].sum()\n",
407 | " / dt.loc[dt['tradeIs'] == 'Bad', 'resultat%'].count(), 2)\n",
408 | " idworst = dt.loc[dt['tradeIs'] == 'Bad', 'resultat%'].idxmin()\n",
409 | " worstTrade = round(dt.loc[dt['tradeIs'] == 'Bad', 'resultat%'].min(), 2)\n",
410 | "except:\n",
411 | " TotalBadTrades = 0\n",
412 | " AveragePercentageNegativTrades = 0\n",
413 | " idworst = ''\n",
414 | " worstTrade = 0\n",
415 | " print(\"/!\\ There is no Bad Trades in your BackTest, maybe a problem...\")\n",
416 | "\n",
417 | "totalTrades = TotalBadTrades + TotalGoodTrades\n",
418 | "\n",
419 | "try:\n",
420 | " TotalLongTrades = dt.groupby('position')['date'].nunique()['LONG']\n",
421 | " AverageLongTrades = round(dt.loc[dt['position'] == 'LONG', 'resultat%'].sum()\n",
422 | " / dt.loc[dt['position'] == 'LONG', 'resultat%'].count(), 2)\n",
423 | " idBestLong = dt.loc[dt['position'] == 'LONG', 'resultat%'].idxmax()\n",
424 | " bestLongTrade = str(\n",
425 | " round(dt.loc[dt['position'] == 'LONG', 'resultat%'].max(), 2))\n",
426 | " idWorstLong = dt.loc[dt['position'] == 'LONG', 'resultat%'].idxmin()\n",
427 | " worstLongTrade = str(\n",
428 | " round(dt.loc[dt['position'] == 'LONG', 'resultat%'].min(), 2))\n",
429 | "except:\n",
430 | " AverageLongTrades = 0\n",
431 | " TotalLongTrades = 0\n",
432 | " bestLongTrade = ''\n",
433 | " idBestLong = ''\n",
434 | " idWorstLong = ''\n",
435 | " worstLongTrade = ''\n",
436 | " print(\"/!\\ There is no LONG Trades in your BackTest, maybe a problem...\")\n",
437 | "\n",
438 | "try:\n",
439 | " TotalShortTrades = dt.groupby('position')['date'].nunique()['SHORT']\n",
440 | " AverageShortTrades = round(dt.loc[dt['position'] == 'SHORT', 'resultat%'].sum()\n",
441 | " / dt.loc[dt['position'] == 'SHORT', 'resultat%'].count(), 2)\n",
442 | " idBestShort = dt.loc[dt['position'] == 'SHORT', 'resultat%'].idxmax()\n",
443 | " bestShortTrade = str(\n",
444 | " round(dt.loc[dt['position'] == 'SHORT', 'resultat%'].max(), 2))\n",
445 | " idWorstShort = dt.loc[dt['position'] == 'SHORT', 'resultat%'].idxmin()\n",
446 | " worstShortTrade = str(\n",
447 | " round(dt.loc[dt['position'] == 'SHORT', 'resultat%'].min(), 2))\n",
448 | "except:\n",
449 | " AverageShortTrades = 0\n",
450 | " TotalShortTrades = 0\n",
451 | " bestShortTrade = ''\n",
452 | " idBestShort = ''\n",
453 | " idWorstShort = ''\n",
454 | " worstShortTrade = ''\n",
455 | " print(\"/!\\ There is no SHORT Trades in your BackTest, maybe a problem...\")\n",
456 | "\n",
457 | "try:\n",
458 | " totalGoodLongTrade = dt.groupby(['position', 'tradeIs']).size()['LONG']['Good']\n",
459 | "except:\n",
460 | " totalGoodLongTrade = 0\n",
461 | " print(\"/!\\ There is no good LONG Trades in your BackTest, maybe a problem...\")\n",
462 | "\n",
463 | "try:\n",
464 | " totalBadLongTrade = dt.groupby(['position', 'tradeIs']).size()['LONG']['Bad']\n",
465 | "except:\n",
466 | " totalBadLongTrade = 0\n",
467 | " print(\"/!\\ There is no bad LONG Trades in your BackTest, maybe a problem...\")\n",
468 | "\n",
469 | "try:\n",
470 | " totalGoodShortTrade = dt.groupby(['position', 'tradeIs']).size()['SHORT']['Good']\n",
471 | "except:\n",
472 | " totalGoodShortTrade = 0\n",
473 | " print(\"/!\\ There is no good SHORT Trades in your BackTest, maybe a problem...\")\n",
474 | "\n",
475 | "try:\n",
476 | " totalBadShortTrade = dt.groupby(['position', 'tradeIs']).size()['SHORT']['Bad']\n",
477 | "except:\n",
478 | " totalBadShortTrade = 0\n",
479 | " print(\"/!\\ There is no bad SHORT Trades in your BackTest, maybe a problem...\")\n",
480 | "\n",
481 | "TotalTrades = TotalGoodTrades + TotalBadTrades\n",
482 | "winRateRatio = (TotalGoodTrades/TotalTrades) * 100\n",
483 | "\n",
484 | "reasons = dt['reason'].unique()\n",
485 | "\n",
486 | "print(\"BackTest finished, final wallet :\",wallet,\"$\")\n",
487 | "dt\n"
488 | ],
489 | "execution_count": null,
490 | "outputs": []
491 | },
492 | {
493 | "cell_type": "markdown",
494 | "metadata": {
495 | "id": "jSO-P5zDk-Pl"
496 | },
497 | "source": [
498 | "Print Complete BackTest Analyses
"
499 | ]
500 | },
501 | {
502 | "cell_type": "code",
503 | "metadata": {
504 | "colab": {
505 | "base_uri": "https://localhost:8080/"
506 | },
507 | "id": "nj2MK1PPk-Pn",
508 | "outputId": "e21af087-9c1c-46d4-d032-46c702f418c1"
509 | },
510 | "source": [
511 | "print(\"Pair Symbol :\",pairName,)\n",
512 | "print(\"Period : [\" + str(dfTest.index[0]) + \"] -> [\" +\n",
513 | " str(dfTest.index[len(dfTest)-1]) + \"]\")\n",
514 | "print(\"Starting balance :\", initalWallet, \"$\")\n",
515 | "\n",
516 | "print(\"\\n----- General Informations -----\")\n",
517 | "print(\"Final balance :\", round(wallet, 2), \"$\")\n",
518 | "print(\"Performance vs US Dollar :\", round(algoPercentage*100, 2), \"%\")\n",
519 | "print(\"Buy and Hold Performence :\", round(holdPercentage*100, 2),\n",
520 | " \"% | with Leverage :\", round(holdPercentage*100, 2)*leverage, \"%\")\n",
521 | "print(\"Performance vs Buy and Hold :\", round(vsHoldPercentage, 2), \"%\")\n",
522 | "print(\"Best trade : +\"+bestTrade, \"%, the \", idbest)\n",
523 | "print(\"Worst trade :\", worstTrade, \"%, the \", idworst)\n",
524 | "print(\"Worst drawBack :\", str(100*round(dt['drawBack'].min(), 2)), \"%\")\n",
525 | "print(\"Total fees : \", round(dt['frais'].sum(), 2), \"$\")\n",
526 | "\n",
527 | "print(\"\\n----- Trades Informations -----\")\n",
528 | "print(\"Total trades on period :\",totalTrades)\n",
529 | "print(\"Number of positive trades :\", TotalGoodTrades)\n",
530 | "print(\"Number of negative trades : \", TotalBadTrades)\n",
531 | "print(\"Trades win rate ratio :\", round(winRateRatio, 2), '%')\n",
532 | "print(\"Average trades performance :\",tradesPerformance,\"%\")\n",
533 | "print(\"Average positive trades :\", AveragePercentagePositivTrades, \"%\")\n",
534 | "print(\"Average negative trades :\", AveragePercentageNegativTrades, \"%\")\n",
535 | "\n",
536 | "print(\"\\n----- LONG Trades Informations -----\")\n",
537 | "print(\"Number of LONG trades :\",TotalLongTrades)\n",
538 | "print(\"Average LONG trades performance :\",AverageLongTrades, \"%\")\n",
539 | "print(\"Best LONG trade +\"+bestLongTrade, \"%, the \", idBestLong)\n",
540 | "print(\"Worst LONG trade\", worstLongTrade, \"%, the \", idWorstLong)\n",
541 | "print(\"Number of positive LONG trades :\",totalGoodLongTrade)\n",
542 | "print(\"Number of negative LONG trades :\",totalBadLongTrade)\n",
543 | "print(\"LONG trade win rate ratio :\", round(totalGoodLongTrade/TotalLongTrades*100, 2), '%')\n",
544 | "\n",
545 | "print(\"\\n----- SHORT Trades Informations -----\")\n",
546 | "print(\"Number of SHORT trades :\",TotalShortTrades)\n",
547 | "print(\"Average SHORT trades performance :\",AverageShortTrades, \"%\")\n",
548 | "print(\"Best SHORT trade +\"+bestShortTrade, \"%, the \", idBestShort)\n",
549 | "print(\"Worst SHORT trade\", worstShortTrade, \"%, the \", idWorstShort)\n",
550 | "print(\"Number of positive SHORT trades :\",totalGoodShortTrade)\n",
551 | "print(\"Number of negative SHORT trades :\",totalBadShortTrade)\n",
552 | "print(\"SHORT trade win rate ratio :\", round(totalGoodShortTrade/TotalShortTrades*100, 2), '%')\n",
553 | "\n",
554 | "print(\"\\n----- Trades Reasons -----\")\n",
555 | "reasons = dt['reason'].unique()\n",
556 | "for r in reasons:\n",
557 | " print(r+\" number :\", dt.groupby('reason')['date'].nunique()[r])"
558 | ],
559 | "execution_count": null,
560 | "outputs": [
561 | {
562 | "output_type": "stream",
563 | "name": "stdout",
564 | "text": [
565 | "Pair Symbol : ETHUSDT\n",
566 | "Period : [2017-08-17 04:00:00] -> [2021-10-17 12:00:00]\n",
567 | "Starting balance : 1000 $\n",
568 | "\n",
569 | "----- General Informations -----\n",
570 | "Final balance : 82236.09 $\n",
571 | "Performance vs US Dollar : 8123.61 %\n",
572 | "Buy and Hold Performence : 1174.67 % | with Leverage : 2349.34 %\n",
573 | "Performance vs Buy and Hold : 250.04 %\n",
574 | "Best trade : +132.66 %, the 2021-01-11 07:00:00\n",
575 | "Worst trade : -6.29 %, the 2017-10-29 11:00:00\n",
576 | "Worst drawBack : -59.0 %\n",
577 | "Total fees : 13696.87 $\n",
578 | "\n",
579 | "----- Trades Informations -----\n",
580 | "Total trades on period : 293\n",
581 | "Number of positive trades : 73\n",
582 | "Number of negative trades : 220\n",
583 | "Trades win rate ratio : 24.91 %\n",
584 | "Average trades performance : 3.32 %\n",
585 | "Average positive trades : 29.83 %\n",
586 | "Average negative trades : -5.47 %\n",
587 | "\n",
588 | "----- LONG Trades Informations -----\n",
589 | "Number of LONG trades : 146\n",
590 | "Average LONG trades performance : 6.19 %\n",
591 | "Best LONG trade +132.66 %, the 2021-01-11 07:00:00\n",
592 | "Worst LONG trade -6.27 %, the 2020-10-28 08:00:00\n",
593 | "Number of positive LONG trades : 41\n",
594 | "Number of negative LONG trades : 105\n",
595 | "LONG trade win rate ratio : 28.08 %\n",
596 | "\n",
597 | "----- SHORT Trades Informations -----\n",
598 | "Number of SHORT trades : 147\n",
599 | "Average SHORT trades performance : 0.47 %\n",
600 | "Best SHORT trade +104.4 %, the 2018-04-08 12:00:00\n",
601 | "Worst SHORT trade -6.29 %, the 2017-10-29 11:00:00\n",
602 | "Number of positive SHORT trades : 32\n",
603 | "Number of negative SHORT trades : 115\n",
604 | "SHORT trade win rate ratio : 21.77 %\n",
605 | "\n",
606 | "----- Trades Reasons -----\n",
607 | "Open Long Market number : 147\n",
608 | "Close Long Market number : 69\n",
609 | "Open Short Market number : 147\n",
610 | "Stop Loss Short number : 89\n",
611 | "Close Short Market number : 58\n",
612 | "Stop Loss Long number : 77\n"
613 | ]
614 | }
615 | ]
616 | },
617 | {
618 | "cell_type": "markdown",
619 | "metadata": {
620 | "id": "U3WYP8irk-Po"
621 | },
622 | "source": [
623 | "Plot to improve ...
"
624 | ]
625 | },
626 | {
627 | "cell_type": "code",
628 | "metadata": {
629 | "colab": {
630 | "base_uri": "https://localhost:8080/",
631 | "height": 606
632 | },
633 | "id": "biPtfHJMk-Po",
634 | "outputId": "ea0a2b04-7075-473a-b42e-dd51d47834b1"
635 | },
636 | "source": [
637 | "dt[['wallet', 'price']].plot(subplots=True, figsize=(20, 10))\n",
638 | "print(\"\\n----- Plot -----\")"
639 | ],
640 | "execution_count": null,
641 | "outputs": [
642 | {
643 | "output_type": "stream",
644 | "name": "stdout",
645 | "text": [
646 | "\n",
647 | "----- Plot -----\n"
648 | ]
649 | },
650 | {
651 | "output_type": "display_data",
652 | "data": {
653 | "image/png": "iVBORw0KGgoAAAANSUhEUgAABJAAAAIqCAYAAAB7UbAAAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4yLjIsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+WH4yJAAAgAElEQVR4nOzdeXzcVb3/8deZJTPZm2Ztm+5Nd0pLS1uWsrTsKrgAoqiACF4QcbleRX/XiwvueK9wVbgqCAiIgCioYIGyWLbStCxd0jbpnjR7mmWyznJ+f8wkTdqs7aSZad7Ph3105sz5fr9n2oL27ed8jrHWIiIiIiIiIiIi0hfHSC9ARERERERERERimwIkERERERERERHplwIkERERERERERHplwIkERERERERERHplwIkERERERERERHplwIkERERERERERHpl2ukF3C0srKy7JQpU0Z6GSIiIiIiIiIiJ4wNGzbUWGuzDx+P2wBpypQpFBYWjvQyREREREREREROGMaYvb2NawubiIiIiIiIiIj0SwGSiIiIiIiIiIj0SwGSiIiIiIiIiIj0K257IImIiIiIiIiIHA2/309paSltbW0jvZQR4/V6yc/Px+12D2q+AiQRERERERERGVVKS0tJTU1lypQpGGNGejnHnbWW2tpaSktLmTp16qCu0RY2ERERERERERlV2trayMzMHJXhEYAxhszMzCFVYClAEhEREREREZFRZ7SGR52G+v0VIImIiIiIiIhITHu9pIblP1zDkjte5NP3rRvp5YyYc845h8LCQgCmTJlCTU1Nv/N/+MMfRu3ZCpBEREREREREJKa9vK2KuuYOJo1NZG1xDa0dwZFeUlxQgCQiIiIiIiIio0ZxlY8ZOSlcvWwyAFVN8X162s9+9jPuvvtuAL7yla+wcuVKAF566SWuvvpqbrrpJpYsWcK8efO4/fbbB7zfww8/zNKlS1m4cCGf//znCQaD3HbbbbS2trJw4UKuvvrqY16zTmETERERERERkZhWUuVjyZQMctO8AFQ2tjM5Mzkq9/7u37aw9UBjVO7Vae74NG7/0Lw+P1+xYgU///nPufXWWyksLKS9vR2/38/atWs566yzuOKKKxg7dizBYJBVq1bx/vvvs2DBgl7vVVRUxJ/+9Cdef/113G43N998M4888gg//vGP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TjY3qrEgOacWuN7dv+rutwHvA9sDPyJqV2FRDP0RYLGZDU7et5b4knKhuy9q9lZLfamfC0QG999zmq/VUl/q54JTn334IjPrD+wE3A2c6u6Xu/vi7DRd6kH9XCAyGCwsa9aGS72onyWXZToDSSebheMt4H4zK06evwIMdPc7gGIzuyzZsfUHalLDG9z9UXd/MRsNlgZRPxcO7b8Lg/q5sNRnH17r7nPc/U13P8fd38lSm6X+1M+FQ8HCwqB+lpyV6QCSTjYLhLuvcfd1nq5XdTiRNglwPrCzmT0O3ANMgv/OCiJ5RP1cULT/Lgzq5wJSz334W6B9eD5SPxcUBQsLg/pZclZJJj/MY7aWug4H3k0enw9cmBzAhpMU/TIzc1cl+HyV7Ngc6A08lixeBXwP2BX4zJP6N+rn/KV+bvm0/y4M6ufCpH14YVA/t3zahxcG9bPksowGkFJ0ACsotUArYAmwm5ndACwFLnP3l7PaMskk9XOB0P67MKifC4724YVB/VwgtA8vDOpnyUVNEkBCB7CC4e5uZnsAZxJjc//s7n/KcrMkw9TPBUX778Kgfi4g2ocXBvVzQdE+vDConyXnWFMFK81sX+DV5KYDWAuWFG47G/iVu6/LdnukaaifC4f234VB/VxYtA8vDOrnwqF9eGFQP0uuacoAkg5gIiJ5SPvvwqB+FhHJX9qHFwb1s+SaJgsgiYiIiIiIiIhIy1CU7QaIiIiIiIiIiEhuUwBJRERERERERES2SAEkERERERERERHZIgWQRERERERERERkixRAEhEREckAM7vazL61hdc/Z2YjmrNNIiIiIpmiAJKIiIhI8/gcoACSiIiI5CVz92y3QURERCQvmdn3gXOBRcBs4C1gBXAR0AqYBpwNjAIeT15bAZySfMRNQE9gDXChu3/cnO0XERER2VYKIImIiIg0gJntBdwB7AOUAJOAPwB/dvelyTrXAgvd/bdmdgfwuLs/kLz2b+Bid59qZvsAP3P3Q5v/NxERERHZupJsN0BEREQkTx0IPOzuawDM7LFk+a5J4KgL0AF4asM3mlkHYH/g72aWWty6yVssIiIi0kAKIImIiIhk1h3A59x9spmdB4zbxDpFwHJ3H9WM7RIRERFpMBXRFhEREWmYF4HPmVlbM+sIHJ8s7wjMN7NS4Mw6669KXsPdVwKfmdmpABZ2b76mi4iIiNSPAkgiIiIiDeDuk4D7gMnAP4EJyUs/BN4AXgHqFsW+F7jSzN42sx2I4NIFZjYZ+AA4sbnaLiIiIlJfKqItIiIiIiIiIiJbpAwkERERERERERHZIgWQRERERERERERkixRAEhERERERERGRLVIASUREREREREREtkgBJBERERERERER2SIFkEREREREREREZIsUQBIRERERERERkS1SAElERERERERERLbo/wNKiI3LoZHpNwAAAABJRU5ErkJggg==\n",
654 | "text/plain": [
655 | ""
656 | ]
657 | },
658 | "metadata": {
659 | "needs_background": "light"
660 | }
661 | }
662 | ]
663 | }
664 | ]
665 | }
--------------------------------------------------------------------------------
/exemples/aligatorPerp/aligatorPerpLive.py:
--------------------------------------------------------------------------------
1 | from cBot_perp_ftx import cBot_perp_ftx
2 | import ta
3 | import pandas as pd
4 |
5 | ftx = cBot_perp_ftx(
6 | apiKey='',
7 | secret='',
8 | subAccountName=''
9 | )
10 |
11 | # -- Strategy variable --
12 | perpSymbol = 'ETH-PERP'
13 | leverage = 2
14 |
15 | # -- Price Data --
16 | df = ftx.get_last_historical(perpSymbol, '1h', 250)
17 |
18 | # -- indicators --
19 | df['EMA1']=ta.trend.ema_indicator(close=df['close'], window=7)
20 | df['EMA2']=ta.trend.ema_indicator(close=df['close'], window=30)
21 | df['EMA3']=ta.trend.ema_indicator(close=df['close'], window=50)
22 | df['EMA4']=ta.trend.ema_indicator(close=df['close'], window=100)
23 | df['EMA5']=ta.trend.ema_indicator(close=df['close'], window=121)
24 | df['EMA6']=ta.trend.ema_indicator(close=df['close'], window=200)
25 | df['STOCH_RSI'] = ta.momentum.stochrsi(close=df['close'], window=14, smooth1=3, smooth2=3)
26 |
27 | # -- Condition to open Market LONG --
28 | def openLongCondition(row):
29 | if (row['EMA1'] > row['EMA2']
30 | and row['EMA2'] > row['EMA3']
31 | and row['EMA3'] > row['EMA4']
32 | and row['EMA4'] > row['EMA5']
33 | and row['EMA5'] > row['EMA6']
34 | and row['STOCH_RSI'] < 0.82):
35 | return True
36 | else:
37 | return False
38 |
39 | # -- Condition to close Market LONG --
40 | def closeLongCondition(row):
41 | if row['EMA6'] > row['EMA1']:
42 | return True
43 | else:
44 | return False
45 |
46 | # -- Condition to open Market SHORT --
47 | def openShortCondition(row):
48 | if ( row['EMA6'] > row['EMA5']
49 | and row['EMA5'] > row['EMA4']
50 | and row['EMA4'] > row['EMA3']
51 | and row['EMA3'] > row['EMA2']
52 | and row['EMA2'] > row['EMA1']
53 | and row['STOCH_RSI'] > 0.2 ):
54 | return True
55 | else:
56 | return False
57 |
58 | # -- Condition to close Market SHORT --
59 | def closeShortCondition(row):
60 | if row['EMA1'] > row['EMA6']:
61 | return True
62 | else:
63 | return False
64 |
65 | # -- Get USD amount on Sub Account --
66 | usdAmount = ftx.get_balance_of_one_coin('USD')
67 |
68 | # -- Get actual price --
69 | actualPrice = df.iloc[-1]['close']
70 |
71 | # -- Check if you have no position running --
72 | if len(ftx.get_open_position([perpSymbol])) == 0:
73 | # -- Check if you have to open a LONG --
74 | if openLongCondition(df.iloc[-2]):
75 | # -- Cancel all order (stop loss) --
76 | ftx.cancel_all_open_order(perpSymbol)
77 | # -- Define the quantity max of token from your usd balance --
78 | quantityMax = float(usdAmount)/actualPrice
79 | # -- Create a market order Long --
80 | longOrder = ftx.place_market_order(
81 | perpSymbol,
82 | 'buy',
83 | quantityMax,
84 | leverage
85 | )
86 | print("Open a market LONG at", actualPrice)
87 | # -- Create a market stop loss -3% --
88 | stopLoss = ftx.place_market_stop_loss(
89 | perpSymbol,
90 | 'sell',
91 | quantityMax,
92 | actualPrice - 0.03 * actualPrice,
93 | leverage
94 | )
95 | print("Place a Stop Loss at ", actualPrice - 0.03 * actualPrice)
96 |
97 | elif openShortCondition(df.iloc[-2]):
98 | # -- Cancel all order (stop loss) --
99 | ftx.cancel_all_open_order(perpSymbol)
100 | # -- Define the quantity max of token from your usd balance --
101 | quantityMax = float(usdAmount)/actualPrice
102 | # -- Create a market order Long --
103 | shortOrder = ftx.place_market_order(
104 | perpSymbol,
105 | 'sell',
106 | quantityMax,
107 | leverage
108 | )
109 | print("Open a market SHORT at", actualPrice)
110 | # -- Create a market stop loss -3% --
111 | stopLoss = ftx.place_market_stop_loss(
112 | perpSymbol,
113 | 'buy',
114 | quantityMax,
115 | actualPrice + 0.03 * actualPrice,
116 | leverage
117 | )
118 | print("Place a Stop Loss at", actualPrice + 0.03 * actualPrice)
119 |
120 | else:
121 | print("No opportunity to take")
122 |
123 | else:
124 | # -- Check if you have a LONG open --
125 | if ftx.get_open_position([perpSymbol])[0]['side'] == 'long':
126 | # -- Check if you have to close your LONG --
127 | if closeLongCondition(df.iloc[-2]):
128 | ftx.close_all_open_position([perpSymbol])
129 | ftx.cancel_all_open_order(perpSymbol)
130 | print('Close my LONG position')
131 | else:
132 | print("A LONG is running and I don't want to stop it")
133 | # -- Check if you have a SHORT open --
134 | elif ftx.get_open_position([perpSymbol])[0]['side'] == 'short':
135 | if closeShortCondition(df.iloc[-2]):
136 | ftx.close_all_open_position([perpSymbol])
137 | ftx.cancel_all_open_order(perpSymbol)
138 | print('Close my SHORT position')
139 | else:
140 | print("A SHORT is running and I don't want to stop it")
141 |
--------------------------------------------------------------------------------
/exemples/aligatorPerp/cBot_perp_ftx.py:
--------------------------------------------------------------------------------
1 | import ccxt
2 | import pandas as pd
3 | import json
4 | import time
5 |
6 | class cBot_perp_ftx():
7 | def __init__(self, apiKey=None, secret=None, subAccountName=None):
8 | ftxAuthObject = {
9 | "apiKey": apiKey,
10 | "secret": secret,
11 | 'headers': {
12 | 'FTX-SUBACCOUNT': subAccountName
13 | }
14 | }
15 | if ftxAuthObject['secret'] == None:
16 | self._auth = False
17 | self._session = ccxt.ftx()
18 | else:
19 | self._auth = True
20 | self._session = ccxt.ftx(ftxAuthObject)
21 | self._session.load_markets()
22 |
23 | def authentication_required(fn):
24 | """Annotation for methods that require auth."""
25 | def wrapped(self, *args, **kwargs):
26 | if not self._auth:
27 | print("You must be authenticated to use this method", fn)
28 | exit()
29 | else:
30 | return fn(self, *args, **kwargs)
31 | return wrapped
32 |
33 | def get_historical_since(self, symbol, timeframe, startDate):
34 | try:
35 | tempData = self._session.fetch_ohlcv(symbol, timeframe, int(
36 | time.time()*1000)-1209600000, limit=1000)
37 | dtemp = pd.DataFrame(tempData)
38 | timeInter = int(dtemp.iloc[-1][0] - dtemp.iloc[-2][0])
39 | except:
40 | return None
41 |
42 | finished = False
43 | start = False
44 | allDf = []
45 | startDate = self._session.parse8601(startDate)
46 | while(start == False):
47 | try:
48 | tempData = self._session.fetch_ohlcv(
49 | symbol, timeframe, startDate, limit=1000)
50 | dtemp = pd.DataFrame(tempData)
51 | timeInter = int(dtemp.iloc[-1][0] - dtemp.iloc[-2][0])
52 | nextTime = int(dtemp.iloc[-1][0] + timeInter)
53 | allDf.append(dtemp)
54 | start = True
55 | except:
56 | startDate = startDate + 1209600000*2
57 |
58 | if dtemp.shape[0] < 1:
59 | finished = True
60 | while(finished == False):
61 | try:
62 | tempData = self._session.fetch_ohlcv(
63 | symbol, timeframe, nextTime, limit=1000)
64 | dtemp = pd.DataFrame(tempData)
65 | nextTime = int(dtemp.iloc[-1][0] + timeInter)
66 | allDf.append(dtemp)
67 | if dtemp.shape[0] < 1:
68 | finished = True
69 | except:
70 | finished = True
71 | result = pd.concat(allDf, ignore_index=True, sort=False)
72 | result = result.rename(
73 | columns={0: 'timestamp', 1: 'open', 2: 'high', 3: 'low', 4: 'close', 5: 'volume'})
74 | result = result.set_index(result['timestamp'])
75 | result.index = pd.to_datetime(result.index, unit='ms')
76 | del result['timestamp']
77 | return result
78 |
79 | def get_last_historical(self, symbol, timeframe, limit):
80 | result = pd.DataFrame(data=self._session.fetch_ohlcv(
81 | symbol, timeframe, None, limit=limit))
82 | result = result.rename(
83 | columns={0: 'timestamp', 1: 'open', 2: 'high', 3: 'low', 4: 'close', 5: 'volume'})
84 | result = result.set_index(result['timestamp'])
85 | result.index = pd.to_datetime(result.index, unit='ms')
86 | del result['timestamp']
87 | return result
88 |
89 | def get_min_order_amount(self, symbol):
90 | return self._session.markets_by_id[symbol]['limits']['amount']['min']
91 |
92 | def convert_amount_to_precision(self, symbol, amount):
93 | return self._session.amount_to_precision(symbol, amount)
94 |
95 | def convert_price_to_precision(self, symbol, price):
96 | return self._session.price_to_precision(symbol, price)
97 |
98 | @authentication_required
99 | def get_all_balance(self):
100 | try:
101 | allBalance = self._session.fetchBalance()
102 | except BaseException as err:
103 | raise TypeError("An error occured in get_all_balance", err)
104 | return allBalance['total']
105 |
106 | @authentication_required
107 | def get_balance_of_one_coin(self, coin):
108 | try:
109 | allBalance = self._session.fetchBalance()
110 | except BaseException as err:
111 | raise TypeError("An error occured in get_balance_of_one_coin", err)
112 | try:
113 | return allBalance['total'][coin]
114 | except:
115 | return 0
116 |
117 | @authentication_required
118 | def place_market_order(self, symbol, side, amount, leverage=1):
119 | try:
120 | return self._session.createOrder(
121 | symbol,
122 | 'market',
123 | side,
124 | self.convert_amount_to_precision(symbol, amount * leverage),
125 | None
126 | )
127 | except BaseException as err:
128 | raise TypeError("An error occured in place_market_order", err)
129 |
130 | @authentication_required
131 | def place_reduce_market_order(self, symbol, side, amount, leverage=1):
132 | params = {
133 | 'reduceOnly':True
134 | }
135 | try:
136 | return self._session.createOrder(
137 | symbol,
138 | 'market',
139 | side,
140 | self.convert_amount_to_precision(symbol, amount * leverage),
141 | None,
142 | params
143 | )
144 | except BaseException as err:
145 | raise TypeError("An error occured in place_reduce_market_order", err)
146 |
147 | @authentication_required
148 | def place_limit_order(self, symbol, side, amount, price, leverage=1):
149 | try:
150 | return self._session.createOrder(
151 | symbol,
152 | 'limit',
153 | side,
154 | self.convert_amount_to_precision(symbol, amount * leverage),
155 | self.convert_price_to_precision(symbol, price)
156 | )
157 | except BaseException as err:
158 | raise TypeError("An error occured in place_limit_order", err)
159 |
160 | @authentication_required
161 | def place_reduce_limit_order(self, symbol, side, amount, price, leverage=1):
162 | params = {
163 | 'reduceOnly':True
164 | }
165 | try:
166 | return self._session.createOrder(
167 | symbol,
168 | 'limit',
169 | side,
170 | self.convert_amount_to_precision(symbol, amount * leverage),
171 | self.convert_price_to_precision(symbol, price),
172 | params
173 | )
174 | except BaseException as err:
175 | raise TypeError("An error occured in place_reduce_limit_order", err)
176 |
177 | @authentication_required
178 | def place_market_stop_loss(self, symbol, side, amount, price, leverage=1):
179 | params = {
180 | 'stopPrice': self.convert_price_to_precision(symbol, price), # your stop price
181 | 'reduceOnly':True
182 | }
183 | try:
184 | return self._session.createOrder(
185 | symbol,
186 | 'stop',
187 | side,
188 | self.convert_amount_to_precision(symbol, amount * leverage),
189 | None,
190 | params
191 | )
192 | except BaseException as err:
193 | raise TypeError("An error occured in place_market_stop_loss", err)
194 |
195 | @authentication_required
196 | def place_market_take_profit(self, symbol, side, amount, price, leverage=1):
197 | params = {
198 | 'stopPrice': self.convert_price_to_precision(symbol, price), # your stop price
199 | 'reduceOnly':True
200 | }
201 | try:
202 | return self._session.createOrder(
203 | symbol,
204 | 'takeProfit',
205 | side,
206 | self.convert_amount_to_precision(symbol, amount * leverage),
207 | None,
208 | params
209 | )
210 | except BaseException as err:
211 | raise TypeError("An error occured in place_market_take_profit", err)
212 |
213 |
214 | @authentication_required
215 | def cancel_all_open_order(self, symbol):
216 | try:
217 | return self._session.cancel_all_orders(symbol)
218 | except BaseException as err:
219 | raise TypeError("An error occured in cancel_all_open_order", err)
220 |
221 | @authentication_required
222 | def cancel_order_by_id(self, id):
223 | try:
224 | return self._session.cancel_order(id)
225 | except BaseException as err:
226 | raise TypeError("An error occured in cancel_order_by_id", err)
227 |
228 | @authentication_required
229 | def get_open_order(self, symbol=None):
230 | try:
231 | return self._session.fetchOpenOrders(symbol, None, None)
232 | except BaseException as err:
233 | raise TypeError("An error occured in get_open_order", err)
234 |
235 | @authentication_required
236 | def get_open_conditionnal_order(self, symbol=None):
237 | params = {
238 | 'type':'stop'
239 | }
240 | try:
241 | return self._session.fetchOpenOrders(symbol,None,None,params)
242 | except BaseException as err:
243 | raise TypeError("An error occured in get_open_conditionnal_order", err)
244 |
245 | @authentication_required
246 | def get_my_trades(self, symbol=None, since=None, limit=1):
247 | try:
248 | return self._session.fetch_my_trades(symbol, since, limit)
249 | except BaseException as err:
250 | raise TypeError("An error occured in get_my_trades", err)
251 |
252 | @authentication_required
253 | def get_open_position(self,symbol=None):
254 | try:
255 | positions = self._session.fetchPositions(symbol)
256 | truePositions = []
257 | for position in positions:
258 | if float(position['contracts']) > 0:
259 | truePositions.append(position)
260 | return truePositions
261 | except BaseException as err:
262 | raise TypeError("An error occured in get_open_position", err)
263 |
264 | @authentication_required
265 | def close_all_open_position(self,symbol=None):
266 | try:
267 | positions = self._session.fetchPositions(symbol)
268 | for position in positions:
269 | if position['side'] == 'long' and position['contracts'] > 0:
270 | self.place_reduce_market_order(position['symbol'], 'sell', position['contracts'])
271 | elif position['side'] == 'short' and position['contracts'] > 0:
272 | self.place_reduce_market_order(position['symbol'], 'buy', position['contracts'])
273 | return 'Close all positions done'
274 | except BaseException as err:
275 | raise TypeError("An error occured in close_all_open_position", err)
276 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | ccxt==1.58.96
2 | pandas==1.3.4
3 |
--------------------------------------------------------------------------------