├── Account.py ├── README.md ├── __init__.py ├── __pycache__ ├── Account.cpython-37.pyc ├── FakeOrder.cpython-37.pyc └── nn.cpython-37.pyc ├── core.py ├── screenshots ├── Code_2020-03-02_19-34-33.png ├── Code_2020-03-02_19-35-05.png └── Code_2020-03-02_19-35-14.png └── settings.ini /Account.py: -------------------------------------------------------------------------------- 1 | import math 2 | import time 3 | def round_down(num,digits): 4 | factor = 10.0 ** digits 5 | return math.floor(num * factor) / factor 6 | 7 | class Account: 8 | def __init__(self,client,settings): 9 | self.createdAt = time.time() 10 | self.id = 0 11 | self.SETTINGS = settings 12 | self.client = client 13 | self.startingBalance = self.getStartingBalance() 14 | self.freeBalance = self.startingBalance 15 | self.orders = [] 16 | self.closedOrders = [] 17 | self.allClosedOrders = [] 18 | def getStartingBalance(self): 19 | return 0.5#float(self.client.get_asset_balance(asset='BTC')) 20 | 21 | @property 22 | def fitness(self): 23 | return self.totalPercentageChange2/self.avgHODLTime if self.avgHODLTime > 0 else self.totalPercentageChange2/(self.avgHODLTime+1) 24 | 25 | @property 26 | def avgHODLTime(self): 27 | v = 0 28 | i = 0 29 | for order in self.orders + self.allClosedOrders: 30 | i += 1 31 | v += order.HODLTime/900 32 | return v/i if i > 0 else v/(i+1) 33 | 34 | @property 35 | def liveTime(self): 36 | return time.time() - self.createdAt 37 | @property 38 | def liveTimeText(self): 39 | v = time.time() - self.createdAt 40 | 41 | 42 | hours = math.floor(v/3600) 43 | minutes = math.floor((v - hours*3600)/60) 44 | secs = int(v - minutes*60 - hours*3600) 45 | t = '' 46 | if hours: 47 | t += f'{hours}h ' 48 | if minutes: 49 | t += f'{minutes}m ' 50 | if secs: 51 | t += f'{secs}s' 52 | return t 53 | 54 | return v 55 | 56 | @property 57 | def BTCBalanceInOrder(self): 58 | b = 0 59 | for order in self.orders: 60 | if order.state in (FOrder.STATE.NEW, FOrder.STATE.FILLED): 61 | b += order.startingBTCBalance 62 | return b 63 | 64 | @property 65 | def currentTotalOrdersBalance(self): 66 | b = 0 67 | for order in self.orders: 68 | b += order.currentBTCBalance 69 | return b 70 | 71 | 72 | 73 | @property 74 | def theroeticalBalance(self): 75 | return self.freeBalance + self.currentTotalOrdersBalance 76 | 77 | @property 78 | def theoreticalProfit(self): 79 | return self.currentTotalOrdersBalance 80 | 81 | @property 82 | def BTCBuyQuantity(self): 83 | return self.startingBalance/5 84 | 85 | def createOrder(self,kline): 86 | if self.getOrder(kline): 87 | return False 88 | raise Exception(f'Order for {kline["s"]} already exists.') 89 | 90 | BTCBuyQuantity = self.BTCBuyQuantity 91 | 92 | if BTCBuyQuantity < 0.001 or self.freeBalance < BTCBuyQuantity: 93 | return False 94 | raise Exception(f'Not enought BTC balance ({self.freeBalance})') 95 | symbol = kline['s'] 96 | symbolInfo = self.client.get_symbol_info(symbol) 97 | 98 | buyQuantity = self.getQuantity(kline,BTCBuyQuantity,symbolInfo) 99 | BTCFinalBuyQuantity = buyQuantity*kline['c'] 100 | self.freeBalance -= BTCFinalBuyQuantity 101 | order = FOrder(self,kline,buyQuantity,BTCBuyQuantity) 102 | self.orders.append(order) 103 | return True 104 | def getQuantity(self,kline,BTCqty,info): 105 | quantity = BTCqty/kline['c'] 106 | stepSize = None 107 | for item in info['filters']: 108 | if 'stepSize' in item.keys(): 109 | stepSize = item['stepSize'] 110 | break 111 | i = 0 112 | for char in stepSize: 113 | if char == '.': 114 | continue 115 | if char == '1': 116 | break 117 | i += 1 118 | 119 | quantity = round_down(quantity,i) 120 | return quantity 121 | 122 | def updateOrder(self,kline): 123 | o = self.getOrder(kline) 124 | if not o: 125 | return False 126 | 127 | o.update(kline) 128 | 129 | def getOrder(self,kline): 130 | for o in self.orders: 131 | if o.symbol == kline['s']: 132 | return o 133 | return None 134 | 135 | @property 136 | def openOrders(self): 137 | c = 0 138 | for o in self.orders: 139 | c += 1 140 | return c 141 | 142 | @property 143 | def openOrdersText(self): 144 | t = '' 145 | for o in self.orders: 146 | t += f'[{o.symbol} {round(o.percentageChange,2)}%] ' 147 | return t 148 | 149 | @property 150 | def closedOrdersProfit(self): 151 | c = 0 152 | for order in self.allClosedOrders: 153 | c += order.profit 154 | return c 155 | 156 | @property 157 | def closedOrdersText(self): 158 | t = '' 159 | for o in self.allClosedOrders: 160 | t += f'[{o.symbol} {round(o.percentageChange,2)}%] ' 161 | return t 162 | 163 | @property 164 | def totalProfit(self): 165 | return self.theroeticalBalance - self.startingBalance 166 | 167 | @property 168 | def totalProfit2(self): 169 | return self.closedOrdersProfit 170 | 171 | @property 172 | def totalPercentageChange2(self): 173 | return ( (self.totalProfit2)/self.startingBalance )*100 174 | 175 | @property 176 | def totalPercentageChange(self): 177 | return ( (self.theroeticalBalance - self.startingBalance)/self.startingBalance )*100 178 | 179 | @property 180 | def text(self): 181 | t = f'Live time: {self.liveTimeText}\n| Profit: {"%.8f"%self.totalProfit2} BTC ({round(self.totalPercentageChange2,2)}%) (Fit: {self.fitness})\nCurrent BTC Balance: {"%.8f"%self.theroeticalBalance}\nStarting BTC Balance: {self.startingBalance}\n| Theor. Profit: {"%.8f"%(self.totalProfit2+self.theoreticalProfit)} BTC \nFree BTC Balance: {"%.8f"%self.freeBalance}\nBTC balance in order: {"%.8f"%self.BTCBalanceInOrder}\nOpen orders: {self.openOrders}\n{self.openOrdersText}\n\nClosed orders: {len(self.allClosedOrders)}\n{self.closedOrdersText}' 182 | return t 183 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PumpDetector 2 | Pump detector can track coin's prices and perform actions based on criteria set by user. It's able to purchase and sell currencies on it's own. It can also act like a visualization tool for two different klines for currencies that meet the requirements to be shown. 3 | 4 | ## Screenshots 5 | ![](screenshots/Code_2020-03-02_19-34-33.png) 6 | ![](screenshots/Code_2020-03-02_19-35-05.png) 7 | ![](screenshots/Code_2020-03-02_19-35-14.png) 8 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | import random 2 | class NN: 3 | def __init__(self,layers=3): 4 | self.layers = [Layer() for _ in range(0,layers)] 5 | self.inputs = None 6 | def input(self,inputs): 7 | self.inputs = inputs 8 | for i,l in enumerate(self.layers): 9 | if i == 0: 10 | l.input(self.inputs) 11 | else: 12 | l.input(self.layers[i-1].vector) 13 | 14 | def addLayer(self,layer): 15 | self.layers.append(layer) 16 | def delLayer(self,layer=None,idx=None): 17 | if idx: 18 | self.layers.pop(idx) 19 | elif layer: 20 | self.layers.remove(layer) 21 | def __str__(self): 22 | t = f"This neural network's current inputs are:\n{self.inputs}\nIt contains {len(self.layers)} layers:\n" 23 | for layer in self.layers: 24 | t += str(layer) + "\n" 25 | t += f'\nNN output: {self.layers[-1].vector}' 26 | return t 27 | 28 | class Layer: 29 | def __init__(self,perceptrons=6): 30 | self.perceptrons = [Perceptron() for _ in range(0,perceptrons)] 31 | self.inputs = None 32 | @property 33 | def vector(self): 34 | return [p.output for p in self.perceptrons] 35 | 36 | def input(self,inputs): 37 | self.inputs = inputs 38 | for p in self.perceptrons: 39 | p.input(inputs) 40 | 41 | def addPerceptron(self,perceptron): 42 | self.perceptrons.append(perceptron) 43 | 44 | def delPerceptron(self,perceptron=None,idx=None): 45 | if perceptron: 46 | self.perceptrons.remove(perceptron) 47 | elif idx: 48 | self.perceptrons.pop(idx) 49 | def __str__(self): 50 | t = '[Layer]\n' 51 | for p in self.perceptrons: 52 | t += str(p) + "\n" 53 | return t 54 | 55 | class Perceptron: 56 | def __init__(self,bias=0): 57 | self.inputs=[] 58 | self.weights=[] 59 | self.output = 0 60 | self.baseWeight = 0 61 | self.bias = bias 62 | 63 | @property 64 | def sum(self): 65 | wx = 0 66 | for w,x in zip(self.weights,self.inputs): 67 | wx += w*x 68 | return wx + self.bias 69 | 70 | def checkParameters(self,inputs): 71 | if len(inputs) > len(self.inputs): 72 | self.weights += [random.randint(0,10) for _ in range(0,len(inputs)-len(self.inputs))] 73 | elif len(inputs) < len(self.inputs): 74 | self.weights = self.weights[:len(inputs)] 75 | 76 | def input(self,inputs): 77 | self.checkParameters(inputs) 78 | self.inputs = inputs 79 | if self.sum > 0: 80 | self.fire() 81 | 82 | def fire(self): 83 | self.output = 1 84 | 85 | def __str__(self): 86 | return f'[P]\ninputs: {self.inputs}\nweights: {self.weights}\nbias: {self.bias}\noutput: {self.output}' 87 | 88 | -------------------------------------------------------------------------------- /__pycache__/Account.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/H3Cki/PumpDetector/da94d7cb16ebfb88713ca695b5f900bb26eed503/__pycache__/Account.cpython-37.pyc -------------------------------------------------------------------------------- /__pycache__/FakeOrder.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/H3Cki/PumpDetector/da94d7cb16ebfb88713ca695b5f900bb26eed503/__pycache__/FakeOrder.cpython-37.pyc -------------------------------------------------------------------------------- /__pycache__/nn.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/H3Cki/PumpDetector/da94d7cb16ebfb88713ca695b5f900bb26eed503/__pycache__/nn.cpython-37.pyc -------------------------------------------------------------------------------- /core.py: -------------------------------------------------------------------------------- 1 | import time 2 | import math 3 | from binance.client import Client 4 | from binance.websockets import BinanceSocketManager 5 | import binance.enums as be 6 | from operator import itemgetter 7 | import threading 8 | from os import system 9 | import json 10 | import os 11 | from win32com.client import Dispatch 12 | import winshell 13 | from Account import Account 14 | import random 15 | 16 | 17 | filename = 'settings.ini' 18 | 19 | SETTINGS = { 20 | 'shortInterval':'15m', 21 | 'longInterval':'1d', 22 | 'minPrice':0.000001, 23 | 'maxShortIncrease': 999.0, 24 | 'maxLongTolerance': 1.0, 25 | 'jumpThresholds':[0.0,2.0,2.5], 26 | 'minSellPercentage': 1.0, 27 | 'maxLossPercentage': -1.0, 28 | 'minTrades': 0, 29 | 'stageForListing':1, 30 | 'printLimit':8, 31 | 'topXHighlighted': 3, 32 | 'minPrintInterval': 1.0, 33 | 'minSecondaryPrintInterval': 1.0, 34 | 'simpleValues': False, 35 | 'simpleValuesFormating': False, 36 | 'waitForNewKline': False, 37 | 'maxKlineTime' : 60, 38 | 'skipMenu': False 39 | } 40 | 41 | client = Client() 42 | bm = BinanceSocketManager(client) 43 | 44 | 45 | 46 | def floatize(kline): 47 | for key in kline['k']: 48 | try: 49 | kline['k'][key] = float(kline['k'][key]) 50 | except: 51 | pass 52 | def spacer(symbol,x=6): 53 | spaces = 6 - len(symbol[:-3]) 54 | return " "*spaces 55 | 56 | def customSpacer(short,lng): 57 | return " "*(len(lng)-len(short)) 58 | 59 | def loadConfig(): 60 | global SETTINGS 61 | if os.path.isfile(filename): 62 | try: 63 | with open(filename,'r') as f: 64 | SETTINGS = json.load(f) 65 | except Exception as e: 66 | input(f"Loading config FAILED: {str(e)}") 67 | saveConfig() 68 | else: 69 | saveConfig() 70 | t = f'start cmd.exe /k "python {os.path.realpath(__file__)}"' 71 | bf_path = os.path.join(os.path.dirname(os.path.abspath(__file__)),'PumpDetector3.bat') 72 | with open(bf_path,'w') as bf: 73 | bf.write(t) 74 | 75 | desktop = winshell.desktop() 76 | path = os.path.join(desktop, "PumpDetector3.lnk") 77 | target = bf_path 78 | wDir = os.path.dirname(os.path.abspath(__file__)) 79 | icon = bf_path 80 | 81 | shell = Dispatch('WScript.Shell') 82 | shortcut = shell.CreateShortCut(path) 83 | shortcut.Targetpath = target 84 | shortcut.WorkingDirectory = wDir 85 | shortcut.IconLocation = icon 86 | shortcut.save() 87 | 88 | def saveConfig(x=SETTINGS): 89 | try: 90 | with open(os.path.join(os.path.dirname(os.path.abspath(__file__)),filename),'w') as cf: 91 | json.dump(x,cf) 92 | except Exception as e: 93 | input(f"Saving config FAILED: {str(e)}") 94 | 95 | loadConfig() 96 | 97 | 98 | 99 | 100 | 101 | def unzero(n): 102 | n = f'{"%.8f"%n}' 103 | e = ['0','.',','] 104 | x = 0 105 | while n[0] in e: 106 | n = n[1:] 107 | x+=1 108 | if SETTINGS['simpleValuesFormating']: 109 | n = (" "*x)+n 110 | return n 111 | 112 | 113 | 114 | class Candle: 115 | baseCandleLen = 57 116 | 117 | def __init__(self,klineObj,lng=False): 118 | self.parent = klineObj 119 | self.lng = lng 120 | 121 | @property 122 | def s(self): 123 | if self.lng: 124 | s = '█' 125 | else: 126 | s = '■' 127 | if self.parent.isPositive(day=self.lng) == 0: 128 | s = '|' 129 | return s 130 | @property 131 | def shadowSymbol(self): 132 | s = '─' 133 | if self.lng: 134 | if self.parent.dayKline['l'] == self.parent.dayKline['h']: 135 | s = ' ' 136 | else: 137 | if self.parent.k['l'] == self.parent.k['h']: 138 | s = ' ' 139 | return s 140 | 141 | @property 142 | def kline(self): 143 | if self.lng: 144 | return self.parent.dayKline 145 | else: 146 | return self.parent.k 147 | @property 148 | def totalCandleLen(self): 149 | return self.kline['h'] - self.kline['l'] 150 | @property 151 | def candleLen(self): 152 | candleLen = Candle.baseCandleLen 153 | if self.lng is False: 154 | totalDayCandleLen = self.parent.dayKlineCandle.totalCandleLen 155 | candleLen = int(candleLen * (self.totalCandleLen/totalDayCandleLen)) 156 | return candleLen 157 | 158 | @property 159 | def bodyCandleSize(self): 160 | return abs(self.kline['o']-self.kline['c']) 161 | @property 162 | def lowerShadowCandleSize(self): 163 | if self.parent.isPositive(day=self.lng) == 1: 164 | lowerShadowCandleSize = self.kline['o'] - self.kline['l'] 165 | elif self.parent.isPositive(day=self.lng) == -1: 166 | lowerShadowCandleSize = self.kline['c'] - self.kline['l'] 167 | else: 168 | lowerShadowCandleSize = self.kline['c'] - self.kline['l'] 169 | return lowerShadowCandleSize 170 | @property 171 | def upperShadowCandleSize(self): 172 | if self.parent.isPositive(day=self.lng) == 1: 173 | upperShadowCandleSize = self.kline['h'] - self.kline['c'] 174 | elif self.parent.isPositive(day=self.lng) == -1: 175 | upperShadowCandleSize = self.kline['h'] - self.kline['o'] 176 | else: 177 | upperShadowCandleSize = self.kline['h'] - self.kline['o'] 178 | return upperShadowCandleSize 179 | 180 | 181 | 182 | @property 183 | def bodyPerc(self): 184 | return self.bodyCandleSize / self.totalCandleLen 185 | @property 186 | def lowerShadowPerc(self): 187 | return self.lowerShadowCandleSize / self.totalCandleLen 188 | @property 189 | def upperShadowPerc(self): 190 | return 1 - self.lowerShadowPerc - self.bodyPerc 191 | 192 | 193 | 194 | @property 195 | def upperShadowLen(self): 196 | return math.floor(self.candleLen*self.upperShadowPerc) 197 | @property 198 | def lowerShadowLen(self): 199 | return self.candleLen - self.bodyLen - self.upperShadowLen 200 | @property 201 | def bodyLen(self): 202 | bodyStrLen = math.ceil(self.candleLen*self.bodyPerc) 203 | if bodyStrLen == 0 or bodyStrLen == 1: 204 | bodyStrLen = 1 205 | 206 | return bodyStrLen 207 | @property 208 | def totalLen(self): 209 | return self.bodyLen + self.lowerShadowLen+ self.upperShadowLen 210 | 211 | 212 | @property 213 | def bodyStr(self): 214 | return self.s*self.bodyLen 215 | @property 216 | def lowerShadowStr(self): 217 | return self.shadowSymbol*self.lowerShadowLen 218 | @property 219 | def upperShadowStr(self): 220 | return self.shadowSymbol*self.upperShadowLen 221 | 222 | @property 223 | def fullCandle(self): 224 | if self.totalCandleLen == 0: 225 | return self.dummyCandle 226 | t = self.lowerShadowStr + self.bodyStr + self.upperShadowStr 227 | 228 | lc = t[-1] 229 | while len(t) < self.candleLen: 230 | t += 'X'#lc 231 | while len(t) > self.candleLen: 232 | t = t[:-1] 233 | 234 | if self.lng is False: 235 | if self.parent.isPositive(day=True) >= 0 and self.parent.isPositive(day=False) >= 0: 236 | nSpaces = (self.parent.dayKlineCandle.lowerShadowLen + self.parent.dayKlineCandle.bodyLen) - (self.lowerShadowLen + self.bodyLen) 237 | elif self.parent.isPositive(day=True) < 0 and self.parent.isPositive(day=False) >= 0: 238 | nSpaces = self.parent.dayKlineCandle.lowerShadowLen - self.lowerShadowLen - self.bodyLen 239 | elif self.parent.isPositive(day=True) < 0 and self.parent.isPositive(day=False) < 0: 240 | nSpaces = self.parent.dayKlineCandle.lowerShadowLen - self.lowerShadowLen 241 | else: 242 | nSpaces = (self.parent.dayKlineCandle.lowerShadowLen + self.parent.dayKlineCandle.bodyLen) - self.lowerShadowLen 243 | 244 | t = " "*nSpaces + t 245 | postSpaces = Candle.baseCandleLen - len(t) 246 | t += " "*postSpaces 247 | 248 | return self.color + t + '\033[0;0m' 249 | 250 | @property 251 | def color(self): 252 | if self.parent.isPositive(day=self.lng) == 1: 253 | colour = '\033[32m' 254 | if self.parent.getLUTickChange(day=self.lng) > 0: 255 | colour = '\033[92m' 256 | elif self.parent.getLUTickChange(day=self.lng) < 0: 257 | colour = '\033[33;40m' 258 | elif self.parent.isPositive(day=self.lng) == -1: 259 | colour = '\033[31m' 260 | if self.parent.getLUTickChange(day=self.lng) > 0: 261 | colour = '\033[1;33;40m' 262 | elif self.parent.getLUTickChange(day=self.lng) < 0: 263 | colour = '\033[1;31;40m' 264 | else: 265 | colour = '' 266 | return colour 267 | 268 | @property 269 | def dummyCandle(self): 270 | return ' '*Candle.baseCandleLen 271 | 272 | class Kline: 273 | class Stage: 274 | colors = ['','\033[1;33;40m','\033[1;36;40m'] 275 | def __init__(self,kline): 276 | self.kline = kline 277 | self.stage = 0 278 | self.jumpThresholds = SETTINGS['jumpThresholds'] 279 | self.listJumpsReq = SETTINGS['stageForListing'] 280 | self.minPrice = SETTINGS['minPrice'] 281 | self.jumps = [0 for _ in range(len(self.jumpThresholds))] 282 | self.maxStage = len(self.jumpThresholds) 283 | self.listed = False 284 | self.prices = [0 for _ in range(len(self.jumpThresholds))] 285 | self.message = '' 286 | self.constMessage = '' 287 | self.checkConditions() 288 | 289 | 290 | @property 291 | def jumpText(self): 292 | js = [f'+{round(j,2)}%' for j in self.jumps if j > 0] 293 | t = f'{self.getColor()}[Jumps: {", ".join(js)}]\033[0;0m' 294 | return t 295 | 296 | def jumpSum(self): 297 | x = 0 298 | for jump in self.jumps: 299 | x += jump 300 | return x 301 | def checkConditions(self): 302 | stage = 0 303 | 304 | 305 | if self.kline.k['c'] < SETTINGS['minPrice'] or self.kline.dayKline is None: 306 | self.listed = False 307 | return 308 | 309 | currentJump = round(self.kline.getOpenClosePercent(),2) 310 | 311 | i = 0 312 | accnr = 0 313 | for jt,j in zip(SETTINGS['jumpThresholds'],self.jumps): 314 | 315 | if j == 0: 316 | if i == 0: 317 | if currentJump > jt and self.kline.getTickChange() > 0: 318 | self.jumps[i] = currentJump 319 | stage += 1 320 | self.message = '' 321 | break 322 | else: 323 | if currentJump > jt and self.kline.getTickChange() > 0 and currentJump > self.jumps[i-1]: 324 | self.jumps[i] = currentJump 325 | stage += 1 326 | break #????????????????? 327 | 328 | elif j > 0 : 329 | self.message = '' 330 | stage += 1 331 | 332 | i += 1 333 | 334 | if stage == len(SETTINGS['jumpThresholds']): 335 | 336 | b = True 337 | if self.kline.getClosePercent24h() > self.kline.getOpenClosePercent()+SETTINGS['maxLongTolerance']: 338 | #self.message = f'> ! LONG TOO HIGH TO BUY ! <' 339 | b = False 340 | if self.kline.getOpenClosePercent() > SETTINGS['maxShortIncrease']: 341 | #self.message = f'> ! SHORT TOO HIGH TO BUY! <' 342 | b = False 343 | if self.kline.getTradesN() < SETTINGS['minTrades']: 344 | #self.message = f'> ! NOT ENOUGH TRADES ! <' 345 | b = False 346 | ''' 347 | if b: 348 | #try: 349 | account.createOrder(self.kline.k)#: 350 | #self.constMessage += f" {self.getColor()}PURCHASED AT {self.kline.close}\033[0;0m" 351 | #self.jumps = [] 352 | #except Exception as e: 353 | #print(e) 354 | if accnr == 0: 355 | self.stage = stage 356 | accnr = 357 | ''' 358 | 359 | 360 | 361 | 362 | 363 | self.stage = stage 364 | #self.stage = stage 365 | 366 | if self.stage >= self.listJumpsReq: 367 | self.listed = True 368 | else: 369 | self.listed = False 370 | 371 | def text(self): 372 | return f'{self.stage}/{self.maxStage}' 373 | def getColor(self): 374 | cols = [37,33,32] 375 | final = 36 376 | if self.stage <= self.listJumpsReq: 377 | return '' 378 | if self.stage == self.maxStage: 379 | return f'\033[1;{final};40m' 380 | return f'\033[1;{cols[int(len(cols)*(self.stage/self.maxStage))-1]};40m' 381 | 382 | 383 | 384 | klines = {} 385 | t = '' 386 | lastPrint = 0 387 | lng = [] 388 | srt = [] 389 | @classmethod 390 | def printAll(cls,force=False): 391 | keys = list(cls.klines.keys()) 392 | if keys == []: 393 | return 394 | if (not len(keys) or Kline.lastPrintDelta() < SETTINGS['minPrintInterval']) and force != True: 395 | return 396 | cls.lastPrint = time.time() 397 | k = keys[0] 398 | t = f'[Pump Detector 3.0] {cls.klines[k].getRemainingKlineTime(_str=True)}\n\n' 399 | 400 | 401 | c = 0 402 | lst = sorted(cls.klines.values(), key=lambda x: (x.stage.stage,x.getOpenClosePercent(),x.getTradesN()),reverse=True) 403 | x = SETTINGS['printLimit'] 404 | if x: 405 | if len(lst) < 6: 406 | x = len(lst) 407 | wls = lst[:x] 408 | else: 409 | wls = lst 410 | i = 0 411 | 412 | ################################################################ 413 | for k in wls: 414 | if cls.klines == {}: 415 | cls.lastPrint = 0 416 | return 417 | if k.stage.listed: 418 | if i < k.no: 419 | up = 1 420 | elif i == k.no: 421 | up = 0 422 | else: 423 | up = -1 424 | k.no = i 425 | t += k.print(i,up=up) 426 | if i+1 == SETTINGS['topXHighlighted'] and SETTINGS['topXHighlighted']: 427 | t+= f'\033[1;30;40mTOP {SETTINGS["topXHighlighted"]} ============================================================= TOP {SETTINGS["topXHighlighted"]} ============================================================= TOP {SETTINGS["topXHighlighted"]}\033[0;0m\n\n' 428 | k.luk = k.k 429 | if k.dayKline: 430 | k.dluk = k.dayKline 431 | i+=1 432 | 433 | t += '\n'# + Detector.accountsText() 434 | #t += '\n' + f'{len(Detector.accounts)} Accounts.' 435 | cls.lastPrint = time.time() 436 | if t != '[Pump Detector 3.0]\n\n': 437 | system('cls') 438 | print(t) 439 | elif force: 440 | pass 441 | #system('cls') 442 | #print("NO JUMPS") 443 | 444 | @classmethod 445 | def lastPrintDelta(cls): 446 | return time.time() - Kline.lastPrint 447 | 448 | @classmethod 449 | def getKline(cls,symbol=None): 450 | return cls.klines.get(symbol) 451 | 452 | @property 453 | def low(self): 454 | if SETTINGS['simpleValues']: 455 | return unzero(self.k["l"]) 456 | else: 457 | return f'{"%.8f"%self.k["l"]}' 458 | 459 | @property 460 | def high(self): 461 | if SETTINGS['simpleValues']: 462 | return unzero(self.k["h"]) 463 | else: 464 | return f'{"%.8f"%self.k["h"]}' 465 | 466 | @property 467 | def close(self): 468 | if SETTINGS['simpleValues']: 469 | return unzero(self.k["c"]) 470 | else: 471 | return f'{"%.8f"%self.k["c"]}' 472 | 473 | @property 474 | def open(self): 475 | if SETTINGS['simpleValues']: 476 | return unzero(self.k["o"]) 477 | else: 478 | return f'{"%.8f"%self.k["o"]}' 479 | 480 | 481 | 482 | 483 | 484 | @property 485 | def dlow(self): 486 | if SETTINGS['simpleValues']: 487 | return unzero(self.dayKline["l"]) 488 | else: 489 | return f'{"%.8f"%self.dayKline["l"]}' 490 | 491 | @property 492 | def dhigh(self): 493 | if SETTINGS['simpleValues']: 494 | return unzero(self.dayKline["h"]) 495 | else: 496 | return f'{"%.8f"%self.dayKline["h"]}' 497 | 498 | @property 499 | def dclose(self): 500 | if SETTINGS['simpleValues']: 501 | return unzero(self.dayKline["c"]) 502 | else: 503 | return f'{"%.8f"%self.dayKline["c"]}' 504 | 505 | @property 506 | def dopen(self): 507 | if SETTINGS['simpleValues']: 508 | return unzero(self.dayKline["o"]) 509 | else: 510 | return f'{"%.8f"%self.dayKline["o"]}' 511 | 512 | 513 | 514 | def __init__(self,kline): 515 | self.no = 999 516 | self.dayKline = None 517 | self.symbol = kline['s'] 518 | self.k = kline['k'] 519 | self.pk = kline['k'] 520 | self.dayKlineCandle = Candle(self,True) 521 | self.Candle = Candle(self) 522 | if Detector.wfnk and SETTINGS['waitForNewKline']: 523 | if self.getKlineTime() > SETTINGS['maxKlineTime']: 524 | Detector._wfnk = True 525 | t = self.getRemainingKlineTime() 526 | x = 0 527 | while x < int(t): 528 | system('cls') 529 | print(f'Waiting {self.getRemainingKlineTime(_str=True)} for new candle.') 530 | time.sleep(1) 531 | #if input('skip...') or 1: 532 | #system("cls") 533 | #print("Waiting for price action...") 534 | #break 535 | 536 | 537 | Detector._wfnk = False 538 | Detector.wfnk = False 539 | self.stage = Kline.Stage(self) 540 | 541 | self.ready = False 542 | self.luk = self.k 543 | self.dluk = self.k 544 | self.dayKlineUpdated = False 545 | self.klineUpdated = False 546 | Kline.klines[self.symbol] = self 547 | 548 | @classmethod 549 | def create(cls,kline): 550 | floatize(kline) 551 | _k = Kline.getKline(kline['s']) 552 | if _k: 553 | _k.update(kline) 554 | return _k 555 | else: 556 | _k = Kline(kline) 557 | return _k 558 | 559 | def update(self,kline=None,day=False): 560 | if self.getRemainingKlineTime() <= 0: 561 | Kline.klines = {} 562 | 563 | 564 | 565 | if day: 566 | self.update24h(kline) 567 | elif kline: 568 | self.pk = self.k 569 | self.k = kline['k'] 570 | self.klineUpdated = True 571 | if self.updatesDone: 572 | self.stage.checkConditions() 573 | if self.stage.listed: 574 | Kline.printAll() 575 | self.dayKlineUpdated = False 576 | self.klineUpdated = False 577 | @property 578 | def updatesDone(self): 579 | if self.dayKlineUpdated == True and self.klineUpdated == True: 580 | return True 581 | return False 582 | def update24h(self,kline): 583 | self.dayKline = kline['k'] 584 | self.dayKlineUpdated = True 585 | def getKlineTime(self): 586 | return (time.time()*1000 - self.k['t'])/1000 587 | def getRemainingKlineTime(self,_str=False): 588 | v = (self.k['T'] - time.time()*1000)/1000 589 | if _str: 590 | 591 | hours = math.floor(v/3600) 592 | minutes = math.floor((v - hours*3600)/60) 593 | secs = int(v - minutes*60 - hours*3600) 594 | t = '' 595 | if hours: 596 | t += f'{hours}h ' 597 | if minutes: 598 | t += f'{minutes}m ' 599 | if secs: 600 | t += f'{secs}s' 601 | return t 602 | 603 | return v 604 | 605 | def getTickChange(self,day=False): 606 | if day: 607 | return self.dayKline['c'] - self.pk['c'] 608 | else: 609 | return self.k['c'] - self.pk['c'] 610 | 611 | def getLUTickChange(self,day=False): 612 | if day: 613 | return self.dayKline['c'] - self.dluk['c'] 614 | else: 615 | return self.k['c'] - self.luk['c'] 616 | 617 | def dummycandle(self,len): 618 | x = '─' 619 | return x*len 620 | 621 | def newHigh(self): 622 | if self.k['h'] > self.pk['h']: 623 | return True 624 | return 625 | 626 | def getVol(self,string=False): 627 | if not self.dayKline: 628 | return '???' 629 | v = float(self.dayKline["q"]) 630 | if string: 631 | v = str(int(v)) 632 | return v 633 | 634 | def getTradesN(self,string=False): 635 | if not self.dayKline: 636 | if string: 637 | return '???' 638 | else: 639 | return 0 640 | v = int(self.k["n"]) 641 | if string: 642 | v = str(v) 643 | return v 644 | 645 | def getLUColor(self): 646 | if self.getLUTickChange() > 0: 647 | return '\033[92m' 648 | elif self.getLUTickChange() < 0: 649 | return '\033[1;31;40m' 650 | return '' 651 | 652 | def print(self,i=None,up=0): 653 | 654 | 655 | candle = self.Candle.fullCandle#self.getCandleText() 656 | candle24 = self.dayKlineCandle.fullCandle#self.getCandleText(day=True) 657 | pp = self.getOpenClosePercent() 658 | ppp = ' ' 659 | if pp > 0: 660 | ppp = '+' 661 | elif pp < 0: 662 | ppp = '' 663 | 664 | ph = self.getOpenHighPercent() 665 | pph = '' 666 | if ph > 0: 667 | pph = '+' 668 | elif ph < 0: 669 | pph = '' 670 | 671 | 672 | 673 | dayx = self.getClosePercent24h(string=True) 674 | dayx2 = self.getOpenHighPercent24h(string=True) 675 | if dayx2 != "???": 676 | dayx2 += f" ({self.dayKline['i']})" 677 | 678 | 679 | 680 | 681 | idx = '' 682 | upt = '-' 683 | uptn = '-' 684 | if up or i is not None: 685 | idx = f'[{str(i+1)}]' 686 | if self.getLUTickChange() > 0: 687 | upt = '\033[92m▲' 688 | uptn = '▲' 689 | elif self.getLUTickChange() < 0: 690 | upt = '\033[1;31;40m▼' 691 | uptn = '▼' 692 | 693 | header_t = f'{self.getLUColor()}{idx}{upt}[\033[1;36;40m{self.symbol}\033[0;0m{self.getLUColor()}]\033[0;0m {spacer(self.symbol)}{self.stage.getColor()}[{self.stage.text()}]\033[0;0m {self.getLUColor()}|{uptn}|\033[0;0m {self.stage.jumpText} {self.getLUColor()}|{uptn}|\033[0;0m Price: {self.getLUColor()}{"%.8f"%self.k["c"]}\033[0;0m ({self.getLUColor()}{ppp}{"%.2f"%pp}%\033[0;0m) {self.stage.constMessage} {self.stage.message}\n' 694 | 695 | if self.dayKline: 696 | candle_td = f' L: {self.dlow} \033[1;37;40m[O: {self.dopen}]\033[0;0m {candle24} \033[1;37;40m[C: {self.dclose}]\033[0;0m H: {self.dhigh} | \033[1;37;40m{dayx} / {dayx2}\033[0;0m\n' 697 | else: 698 | candle_td = '' 699 | 700 | candle_t = f' L: {self.low} \033[1;35;40m[O: {self.open}]\033[0;0m {candle} \033[1;35;40m[C: {self.close}]\033[0;0m H: {self.high} | \033[1;35;40m{ppp}{"%.2f"%pp}% / {pph}{"%.2f"%ph}% ({self.k["i"]})\033[0;0m {self.stage.getColor()}[{self.symbol}]\033[0;0m\n' 701 | footer_t = f' {self.getLUColor()}[?] Vol. {self.getVol()} | Trades. {self.getTradesN()} | {self.getRemainingKlineTime(_str=True)}\033[0;0m\n\n' 702 | 703 | if up == 1: 704 | text = header_t + '\033[92m▲\033[0;0m'+ candle_t[1:] + '\033[92m│\033[0;0m' + candle_td[1:] + '\033[92m│\033[0;0m'+ footer_t[1:] 705 | elif up == -1: 706 | text = header_t + candle_t + '\033[1;31;40m│\033[0;0m'+candle_td[1:] + '\033[1;31;40m▼\033[0;0m' +footer_t[1:] 707 | else: 708 | text = header_t + candle_t + candle_td + footer_t 709 | return text 710 | 711 | def getHighlight(self): 712 | highlight = '' 713 | if self.getOpenClosePercent() > 1: 714 | highlight = '\033[6;30;47m' 715 | if self.newHigh(): 716 | highlight = '\033[6;30;42m' 717 | return highlight 718 | 719 | def getOpenClosePercent(self): 720 | return ((self.k['c']-self.k['o'])/self.k['o'])*100 721 | def getOpenHighPercent(self,day=False): 722 | if day: 723 | return ((self.dayKline['h']-self.dayKline['o'])/self.dayKline['o'])*100 724 | else: 725 | return ((self.k['h']-self.k['o'])/self.k['o'])*100 726 | 727 | def getOpenHighPercent24h(self,string=False): 728 | if self.dayKline is None: 729 | if string: 730 | return "???" 731 | else: 732 | return -1 733 | 734 | v = ((self.dayKline['h']-self.dayKline['o'])/self.dayKline['o'])*100 735 | if string: 736 | 737 | xppp = ' ' 738 | if v > 0: 739 | xppp = '+' 740 | elif v < 0: 741 | xppp = '' 742 | v = f'{xppp}{"%.2f"%v}%' 743 | return v 744 | 745 | 746 | def getClosePercent24h(self,string=False): 747 | if self.dayKline is None: 748 | if string: 749 | return "???" 750 | else: 751 | return -1 752 | 753 | v = ((self.dayKline['c']-self.dayKline['o'])/self.dayKline['o'])*100 754 | if string: 755 | 756 | xppp = ' ' 757 | if v > 0: 758 | xppp = '+' 759 | elif v < 0: 760 | xppp = '' 761 | v = f'{xppp}{"%.2f"%v}%' 762 | return v 763 | 764 | def isPositive(self,day=False): 765 | if day: 766 | kline = self.dayKline 767 | else: 768 | kline = self.k 769 | 770 | if kline['c'] > kline['o']: 771 | return 1 772 | elif kline['c'] < kline['o']: 773 | return -1 774 | return 0 775 | 776 | class Detector: 777 | accounts = [] 778 | exchangeInfo = None 779 | symbols = [] 780 | update = False 781 | wthread = None 782 | fuckup = False 783 | wfnk = SETTINGS['waitForNewKline'] 784 | _wfnk = False 785 | @classmethod 786 | def getExchangeInfo(cls): 787 | cls.exchangeInfo = client.get_exchange_info() 788 | 789 | @classmethod 790 | def getAllSymbols(cls): 791 | if cls.exchangeInfo is None: 792 | cls.getExchangeInfo() 793 | 794 | cls.symbols = [] 795 | cls.symbolOrders = {} 796 | for symbol in cls.exchangeInfo['symbols']: 797 | if symbol['quoteAsset'] == 'BTC':# and symbol['baseAsset'] == 'FTM': 798 | cls.symbols.append(symbol['symbol']) 799 | 800 | 801 | @staticmethod 802 | def handleKlineResponse(kline): 803 | if kline['e'] == 'error': 804 | print(kline['m']) 805 | return 806 | 807 | 808 | x = Kline.create(kline) 809 | 810 | @staticmethod 811 | def waitForKline(*kline): 812 | kline = kline[0] 813 | 814 | if kline['s'] not in Kline.lng: 815 | Kline.lng.append(kline['s']) 816 | 817 | floatize(kline) 818 | 819 | klin = Kline.getKline(kline['s']) 820 | while not klin: 821 | klin = Kline.getKline(kline['s']) 822 | time.sleep(0.1) 823 | 824 | if klin: 825 | klin.update(kline,day=True) 826 | 827 | return 828 | 829 | @staticmethod 830 | def handle24hKlineResponse(kline): 831 | if kline['e'] == 'error': 832 | print(kline['m']) 833 | return 834 | 835 | Detector.wthread = threading.Thread(target=Detector.waitForKline, args=(kline,)) 836 | Detector.wthread.start() 837 | 838 | @classmethod 839 | def updateorders(cls,*kline): 840 | kline = kline[0] 841 | for account in Detector.accounts: 842 | account.updateOrder(kline['k']) 843 | 844 | @staticmethod 845 | def handleKlineResponseMulti(kline): 846 | if Detector._wfnk: 847 | return 848 | if kline['e'] == 'error': 849 | print(kline['m']) 850 | else: 851 | if kline['k']['i'] == SETTINGS['longInterval']: 852 | Detector.handle24hKlineResponse(kline) 853 | else: 854 | Detector.handleKlineResponse(kline) 855 | #threading.Thread(target=Detector.updateorders,args=(kline,)) 856 | 857 | 858 | @classmethod 859 | def startKlineSockets(cls): 860 | cls.connKeys = {} 861 | args = [] 862 | for symbol in cls.symbols: 863 | cls.connKeys[symbol] = [] 864 | c1 = bm.start_kline_socket(symbol, Detector.handleKlineResponseMulti,interval=SETTINGS['shortInterval']) 865 | c2 = bm.start_kline_socket(symbol, Detector.handleKlineResponseMulti,interval=SETTINGS['longInterval']) 866 | cls.connKeys[symbol].append(c1) 867 | cls.connKeys[symbol].append(c1) 868 | bm.start() 869 | 870 | @classmethod 871 | def getTopAcc(cls): 872 | cls.accounts = sorted(cls.accounts, key=lambda x: x.fitness,reverse=True) 873 | 874 | return cls.accounts[0] 875 | 876 | @classmethod 877 | def accountsText(cls): 878 | t = '' 879 | cls.getTopAcc() 880 | for x in range(0,2): 881 | t += f'\n\n[Acc {cls.accounts[x].id}]\n' + cls.accounts[x].text + f"\n{cls.accounts[x].SETTINGS}" 882 | t += f'\n\n[Acc {cls.accounts[-1].id}]\n' + cls.accounts[-1].text + f"\n{cls.accounts[-1].SETTINGS}" 883 | return t 884 | 885 | @classmethod 886 | def s(cls): 887 | while True: 888 | if Kline.klines != {}: 889 | if Kline.lastPrintDelta() >= SETTINGS['minSecondaryPrintInterval'] or Kline.lastPrint == 0: 890 | for key,kline in list(Kline.klines.items()): 891 | if Kline.klines == {}: 892 | break 893 | kline.update() 894 | Kline.printAll(force=True) 895 | cls.accounts = sorted(cls.accounts,key=lambda x: x.fitness, reverse=True) 896 | time.sleep(0.25) 897 | return 898 | 899 | @classmethod 900 | def s2(cls): 901 | global ACCIDX 902 | while True: 903 | time.sleep(3600) 904 | n = 0 905 | 906 | half = int(len(cls.accounts)/10) 907 | cut = len(cls.accounts) - half 908 | 909 | cls.accounts = cls.accounts[:half] 910 | 911 | 912 | print(f'removed {cut} accounts') 913 | for setting in generateRandomSettings(cut): 914 | 915 | acc = Account(client,setting) 916 | acc.id = ACCIDX 917 | cls.accounts.append(acc) 918 | 919 | ACCIDX += 1 920 | return 921 | @classmethod 922 | def run(cls): 923 | global ACCIDX 924 | print('Initializing settings...') 925 | print("Getting exchange info...") 926 | cls.getExchangeInfo() 927 | print("Getting all symbols...") 928 | cls.getAllSymbols() 929 | print("Starting sockets...") 930 | cls.startKlineSockets() 931 | print("Starting loop...") 932 | threading.Thread(target=cls.s).start() 933 | threading.Thread(target=cls.s2).start() 934 | print("Waiting for socket events...") 935 | 936 | 937 | def getIntervals(): 938 | ivs = [] 939 | for k in vars(be).keys(): 940 | if 'INTERVAL' in k: 941 | ivs.append(vars(be)[k]) 942 | return ', '.join(ivs) 943 | 944 | 945 | if SETTINGS['skipMenu'] is False: 946 | x = int(input('1. RUN\n2. HELP\n3. SETTINGS\n') or 1) 947 | while x != 1: 948 | if x == 2: 949 | system('cls') 950 | print('[PUMP DETECTOR 3.0 made by H3Ck1]') 951 | print('\nProgram śledzi wyłącznie kryptowaluty z parą do BTC, odświeżenie każdej z nich odbywa się po otrzymaniu informacji o zmianie ceny. \nParametry programu:\n') 952 | print(f'\n- "shortInterval" - interwał górnej świeczki (krótszej)') 953 | print(f'\n- "longInterval" - interwał dolnej świeczki (dłuższej)') 954 | print(f'\n- "minPrice" - najnizsza cena danej waluty, która pozwala na przejscie do listy obserwowanych') 955 | print(f'\n- "minsellPercentage" - minimalny przelicznik ceny, za którą zostanie sprzedana dana waluta (wartość 1.5 oznacza sprzedanie za cenę równą 101.5% ceny zakupu)') 956 | print(f'\n- "minTrades" - minimalna ilość trejdów w czasie krótkiej świeczki, która pozwoli na zakup waluty.') 957 | print(f'\n- "maxLossPercentage" - maksymalna strata w procentach, która spowoduje sprzedaż.') 958 | 959 | print(f'\n- "maxShortIncrease" - maksymany przyrost (w procentach) krótkiej świeczki, który pozwala na zakup waluty') 960 | print(f'\n- "maxLongTolerance" - maksymalne odchylenie (w procentach) dłuższej świeczki od krótszej, które pozwala na zakup waluty (wartość 1.0 oznacza, że dłuższa świeczka moze być maksymalnie o 1% wyżej od krótszej, jeśli różnica jest większa nie dojdzie do zakupu') 961 | print(f'\n- "jumpThresholds" - progi kolejnych etapów życia waluty (w procentach), każdy kolejny skok musi spełnić kolejny warunek na liście (dla przykładu mamy progi [0.0,2.0,2.5], jeśli waluta wzrośnie o więcej niż 0.0 będzie na etapie 1/3, jeśli następnie przyrost procentowy przekroczy 2.0 etap wzrośnie do 2/3 itd. Przyrost jest liczony od początku świeczki, nie od momentu uruchomienia programu') 962 | print(f'\n- "stageForListing" - etap świeczki, od którego jest wyświetlana na liście obserwowanych') 963 | print(f'\n- "printLimit" - maksymalna ilość wyświetlanych walut z listy obserwowanych') 964 | print(f'\n- "minPrintInterval" - minimalny odstęp czasowy między odświeżeniami listy (krótsze odstępy mogą powodować miganie konsoli i szybki zanik efektów pojawiających się po skokach cen)') 965 | print(f'\n- "minSecondaryPrintInterval" - minimalny odstęp czasowy między odświeżeniami, kiedy waluty z listy obserwowanych stoją w miejscu (aktualizuje czas do końca świeczki)') 966 | print(f'\n- "simpleValues" - usuwa zera z początku ceny kryptowaluty') 967 | print(f'\n- "simpleValuesFormating" - zastępuje usunięte zera spacjami, aby zachować układ') 968 | print(f'\n- "waitForNewKline" - po uruchomieniu program czeka na zakończenie obecnej świeczki') 969 | print(f'\n- "maxKlineTime" - maksymalny dopuszczalny czas istnienia obecnej świeczki (w sekundach), który pomija czekanie na nową (jeśli jest włączona opcja waitForNewKline)') 970 | print(f'\n- "skipMenu" - wyłącza menu po starcie programu') 971 | input('\ncontinue...') 972 | if x == 3: 973 | system('cls') 974 | print(f'SETTINGS: [enter - pominięcie opcji]\nDostępne interwały: {getIntervals()}') 975 | for s in SETTINGS.items(): 976 | if type(s[1]) in (int,float,str): 977 | SETTINGS[s[0]] = type(s[1])(input(f'{s[0]} ({s[1]}): ') or s[1]) 978 | elif type(s[1]) == list: 979 | ipt = (input(f'{s[0]} ({s[1]}): ') or s[1]) 980 | if not isinstance(ipt,list): 981 | items = ipt.replace(' ','') 982 | items = ipt.replace(']','') 983 | items = ipt.replace('[','') 984 | for item in items: 985 | if item == '': 986 | items.remove(item) 987 | try: 988 | items = list(map(float,items.split(','))) 989 | except: 990 | input('ILLEGAL SYMBOL FOUND') 991 | break 992 | else: 993 | items = ipt 994 | SETTINGS[s[0]] = items 995 | elif type(s[1]) == bool: 996 | SETTINGS[s[0]] = type(s[1])(int((input(f'{s[0]} ({int(s[1])}): ') or s[1]))) 997 | print(f'\n[SELECTED SETTINGS]\n') 998 | for s in SETTINGS.items(): 999 | print(f'{s[0]} : {s[1]}') 1000 | input('\nsave...') 1001 | saveConfig(SETTINGS) 1002 | system('cls') 1003 | x = int(input('1. RUN\n2. HELP\n3. SETTINGS\n') or 1) 1004 | 1005 | 1006 | Detector.run() 1007 | 1008 | 1009 | 1010 | 1011 | -------------------------------------------------------------------------------- /screenshots/Code_2020-03-02_19-34-33.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/H3Cki/PumpDetector/da94d7cb16ebfb88713ca695b5f900bb26eed503/screenshots/Code_2020-03-02_19-34-33.png -------------------------------------------------------------------------------- /screenshots/Code_2020-03-02_19-35-05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/H3Cki/PumpDetector/da94d7cb16ebfb88713ca695b5f900bb26eed503/screenshots/Code_2020-03-02_19-35-05.png -------------------------------------------------------------------------------- /screenshots/Code_2020-03-02_19-35-14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/H3Cki/PumpDetector/da94d7cb16ebfb88713ca695b5f900bb26eed503/screenshots/Code_2020-03-02_19-35-14.png -------------------------------------------------------------------------------- /settings.ini: -------------------------------------------------------------------------------- 1 | {"shortInterval": "15m", "longInterval": "1d", "minPrice": 1e-06, "maxShortIncrease": 999.0, "maxLongTolerance": 1.0, "jumpThresholds": [0.0, 2.0, 2.5], "minSellPercentage": 1.0, "maxLossPercentage": -1.0, "minTrades": 0, "stageForListing": 1, "printLimit": 8, "topXHighlighted": 3, "minPrintInterval": 1.0, "minSecondaryPrintInterval": 1.0, "simpleValues": false, "simpleValuesFormating": false, "waitForNewKline": false, "maxKlineTime": 60, "skipMenu": false} --------------------------------------------------------------------------------