├── browser ├── tests.py ├── __init__.py └── browser.py ├── strats ├── __init__.py ├── exceptions.py ├── test_strat.py ├── test_MartingaleStrat.py ├── test_DAlembertStrat.py ├── strats.py └── custom_strats.py ├── stratchecker ├── __init__.py └── stratchecker.py ├── requirements.txt ├── aviator ├── __init__.py ├── vars.py └── aviator.py ├── assets └── aviator-cover.webp ├── docker-compose.yml ├── .gitignore ├── .github └── workflows │ ├── ruff.yml │ └── tests.yml ├── scrape.py ├── autobet.py ├── README.md └── checker.py /browser/tests.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /strats/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /stratchecker/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | helium 2 | rich 3 | matplotlib 4 | -------------------------------------------------------------------------------- /aviator/__init__.py: -------------------------------------------------------------------------------- 1 | from .aviator import Aviator 2 | 3 | __all__ = ["Aviator"] -------------------------------------------------------------------------------- /browser/__init__.py: -------------------------------------------------------------------------------- 1 | from .browser import Browser 2 | 3 | __all__ = ["Browser"] 4 | -------------------------------------------------------------------------------- /assets/aviator-cover.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luisrx7/AviatorStratChecker/HEAD/assets/aviator-cover.webp -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | browser_22bet: 3 | image: selenium/standalone-chrome 4 | ports: 5 | - "4446:4444" 6 | # env_file: 7 | # - ./.env 8 | 9 | environment: 10 | - VNC_NO_PASSWORD=1 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/* 2 | 3 | # Ignore the virtual environment 4 | .venv/* 5 | 6 | # Ignore the pycache 7 | **/__pycache__/* 8 | results0.txt 9 | results1.txt 10 | results2.txt 11 | .txt 12 | creds.py 13 | slices.pickle 14 | autobet.log 15 | -------------------------------------------------------------------------------- /strats/exceptions.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | class InsufficientFunds(Exception): 4 | def __init__(self, message = "Insufficient funds"): 5 | self.message = message 6 | pass 7 | 8 | class MaxBetsReached(Exception): 9 | def __init__(self, message = "Max bets reached"): 10 | self.message = message 11 | pass 12 | -------------------------------------------------------------------------------- /.github/workflows/ruff.yml: -------------------------------------------------------------------------------- 1 | name: Ruff 2 | on: [push, pull_request] 3 | jobs: 4 | ruff: 5 | runs-on: ubuntu-latest 6 | strategy: 7 | matrix: 8 | python-version: ["3.9", "3.10"] 9 | steps: 10 | - uses: actions/checkout@v3 11 | - uses: actions/setup-python@v4 12 | with: 13 | python-version: ${{ matrix.python-version }} 14 | - run: pip install ruff 15 | - run: | 16 | ruff check . --ignore E501 17 | 18 | 19 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: tests 2 | on: [push, pull_request] 3 | jobs: 4 | build: 5 | runs-on: ubuntu-latest 6 | strategy: 7 | matrix: 8 | python-version: ["3.9", "3.10"] 9 | 10 | steps: 11 | - uses: actions/checkout@v3 12 | 13 | - name: Set up Python ${{ matrix.python-version }} 14 | uses: actions/setup-python@v4 15 | with: 16 | python-version: ${{ matrix.python-version }} 17 | 18 | - name: Install dependencies 19 | run: | 20 | python -m pip install --upgrade pip 21 | pip install pytest 22 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi 23 | 24 | - name: Test with pytest 25 | run: | 26 | pytest -------------------------------------------------------------------------------- /scrape.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | import rich 4 | import rich.console 5 | import rich.traceback 6 | 7 | from aviator import Aviator 8 | 9 | rich.traceback.install() 10 | 11 | console = rich.console.Console() 12 | 13 | 14 | logging.basicConfig(format='%(asctime)s %(levelname)s:%(message)s', level=logging.INFO, datefmt='%m/%d/%Y %I:%M:%S') 15 | 16 | 17 | def signal_handler(sig, frame): 18 | global stop 19 | print('You pressed Ctrl+C!') 20 | stop = True 21 | # sys.exit(0) 22 | 23 | 24 | stop = False 25 | 26 | def main(): 27 | 28 | while stop is False: 29 | try: 30 | aviator = Aviator(debug=True) 31 | aviator.login() 32 | aviator.go_to_game() 33 | 34 | 35 | while aviator.in_game() and stop is False: 36 | if aviator.disconnected(): 37 | break 38 | aviator.wait_for_game_to_finish() 39 | aviator.add_to_log(aviator.get_last_game_result()) 40 | except Exception as e: 41 | console.print_exception(show_locals=True) 42 | logging.error(e) 43 | finally: 44 | aviator.close() 45 | 46 | if __name__ == "__main__": 47 | main() -------------------------------------------------------------------------------- /autobet.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import time 3 | 4 | import rich 5 | import rich.console 6 | import rich.traceback 7 | import sys 8 | 9 | from aviator import Aviator 10 | from strats.custom_strats import DAlembertStrat 11 | 12 | rich.traceback.install() 13 | console = rich.console.Console() 14 | 15 | 16 | logging.basicConfig(format='%(asctime)s %(levelname)s:%(message)s', level=logging.INFO, datefmt='%m/%d/%Y %I:%M:%S', filename="autobet.log") 17 | 18 | logging.getLogger().addHandler(logging.StreamHandler(sys.stdout)) 19 | 20 | 21 | def signal_handler(sig, frame): 22 | global stop 23 | print('You pressed Ctrl+C!') 24 | stop = True 25 | # sys.exit(0) 26 | 27 | 28 | stop = False 29 | 30 | 31 | 32 | 33 | def main(): 34 | strat = DAlembertStrat( 35 | description="DAlembertStrat", 36 | start_balance=3000, 37 | base_bet=0.1, 38 | max_bet=0.5, 39 | multiplier=1.9, 40 | max_bets=10000, 41 | ) 42 | 43 | while stop is False: 44 | try: 45 | aviator = Aviator(debug=True, strat=strat) 46 | aviator.login() 47 | aviator.go_to_game() 48 | time.sleep(3) 49 | aviator.setup_auto_bet() 50 | 51 | 52 | while aviator.in_game() and stop is False: 53 | aviator.wait_for_game_to_finish() 54 | last_result = aviator.get_last_game_result() 55 | aviator.process_bet(float(last_result)) 56 | aviator.add_to_log(last_result) 57 | logging.info(f"balance: {aviator.get_balance()}") 58 | except Exception as e: 59 | console.print_exception(show_locals=True) 60 | logging.error(e) 61 | finally: 62 | aviator.close() 63 | 64 | if __name__ == "__main__": 65 | main() -------------------------------------------------------------------------------- /aviator/vars.py: -------------------------------------------------------------------------------- 1 | profile_path = "/tmp/.com.google.Chrome.4QE50k" 2 | 3 | 4 | #game 5 | 6 | play_button_of_first_search_result = '//*[@id="games"]/div/div[1]/button' 7 | play_free_button_of_first_search_result = '//*[@id="games"]/div/div[1]/div[2]/div[1]/button' 8 | 9 | game_name = '//*[@id="slots"]/div[2]/div[1]/div[1]' 10 | 11 | last_game_results = 'body > app-root > app-game > div > div.main-container > div.w-100.h-100 > div > div.game-play > div.result-history.disabled-on-game-focused.my-2 > app-stats-widget > div > div.payouts-wrapper > div > app-bubble-multiplier:nth-child($ROUND$)' 12 | last_game_result = "/html/body/app-root/app-game/div/div[1]/div[2]/div/div[2]/div[1]/app-stats-widget/div/div[1]/div/app-bubble-multiplier[1]/div" 13 | 14 | open_new_window_button = '//*[@id="wrapper_game_area"]/div/ul/li[3]/a' 15 | 16 | balance = '/html/body/app-root/app-game/div/div[1]/div[1]/app-header/div/div[2]/div[1]/div[1]/div/span[1]' 17 | 18 | 19 | #bets 20 | 21 | 22 | bet_type_button = '/html/body/app-root/app-game/div/div[1]/div[2]/div/div[2]/div[3]/app-bet-controls/div/app-bet-control[1]/div/app-navigation-switcher/div/button[2]' 23 | auto_cashout_button = '/html/body/app-root/app-game/div/div[1]/div[2]/div/div[2]/div[3]/app-bet-controls/div/app-bet-control[1]/div/div[3]/div[2]/div[1]/app-ui-switcher' 24 | place_bet_button = '/html/body/app-root/app-game/div/div[1]/div[2]/div/div[2]/div[3]/app-bet-controls/div/app-bet-control[1]/div/div[1]/div[2]/button' 25 | 26 | bet_amount_input_box = '/html/body/app-root/app-game/div/div[1]/div[2]/div/div[2]/div[3]/app-bet-controls/div/app-bet-control[1]/div/div[1]/div[1]/app-spinner/div/div[2]/input' 27 | multiplier_input_box = '/html/body/app-root/app-game/div/div[1]/div[2]/div/div[2]/div[3]/app-bet-controls/div/app-bet-control[1]/div/div[3]/div[2]/div[2]/div/app-spinner/div/div[2]/input' 28 | 29 | 30 | #disconnected 31 | 32 | disconnected_warning = '/html/body/app-root/app-disconnect-message/div/div[2]/div' -------------------------------------------------------------------------------- /strats/test_strat.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from strats.strats import Strat 3 | 4 | class TestStrat(unittest.TestCase): 5 | 6 | def setUp(self): 7 | self.strat = Strat("Test Strat", 1000, 10, 200, 1.5, 100) 8 | 9 | def test_init(self): 10 | self.assertEqual(self.strat.description, "Test Strat") 11 | self.assertEqual(self.strat.start_balance, 1000) 12 | self.assertEqual(self.strat.bet, 10) 13 | 14 | def test_calculate_bet_win(self): 15 | self.strat.calculate_bet(1.7) # 1.7 > 1.5. So, it is a win. 16 | self.assertEqual(self.strat.win_streak, 1) 17 | self.assertEqual(self.strat.lose_streak, 0) 18 | 19 | def test_calculate_bet_lose(self): 20 | self.strat.calculate_bet(1.3) # 1.3 < 1.5. So, it is a lose. 21 | self.assertEqual(self.strat.win_streak, 0) 22 | self.assertEqual(self.strat.lose_streak, 1) 23 | 24 | def test_on_win(self): 25 | self.strat.gamble() # take the bet from the balance 1000 - 10 = 990 26 | self.strat.on_win() 27 | self.assertEqual(self.strat.balance, 1005.0) # 990 + (10 * 1.5) = 1005 28 | self.assertEqual(self.strat.win_streak, 1) 29 | 30 | def test_on_lose(self): 31 | self.strat.gamble() 32 | self.strat.on_lose() 33 | self.assertEqual(self.strat.balance, 990.0) # 1000 - 10 = 990 34 | self.assertEqual(self.strat.lose_count, 1) 35 | 36 | def test_reset(self): 37 | # change the state 38 | self.strat.calculate_bet(1.3) 39 | self.strat.on_lose() 40 | # state is reset 41 | self.strat.reset() 42 | self.assertEqual(self.strat.win_streak, 0) 43 | self.assertEqual(self.strat.lose_streak, 0) 44 | self.assertEqual(self.strat.number_of_bets, 0) 45 | self.assertEqual(self.strat.balance, self.strat.start_balance) 46 | 47 | def test_report(self): 48 | report = self.strat.report_as_string() 49 | self.assertIn("start balance: 1000", report) 50 | self.assertIn("final balance: 1000.0", report) 51 | self.assertIn("won: 0.0", report) 52 | self.assertIn("bet count: 0", report) 53 | 54 | if __name__ == '__main__': 55 | unittest.main() -------------------------------------------------------------------------------- /strats/test_MartingaleStrat.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from strats.custom_strats import MartingaleStrat 3 | 4 | class TestMartingaleStrat(unittest.TestCase): 5 | def setUp(self): 6 | self.start_balance = 1000 7 | self.base_bet = 10 8 | self.max_bet = 100 9 | self.multiplier = 2 10 | self.max_bets = 100 11 | self.strat = MartingaleStrat( 12 | "Martingale Strategy", 13 | self.start_balance, 14 | self.base_bet, 15 | self.max_bet, 16 | self.multiplier, 17 | self.max_bets 18 | ) 19 | 20 | def test_initial_values(self): 21 | self.assertEqual(self.strat.start_balance, self.start_balance) 22 | self.assertEqual(self.strat.balance, self.start_balance) 23 | self.assertEqual(self.strat.base_bet, self.base_bet) 24 | self.assertEqual(self.strat.bet, self.base_bet) 25 | self.assertEqual(self.strat.max_bet, self.max_bet) 26 | self.assertEqual(self.strat.multiplier, self.multiplier) 27 | self.assertEqual(self.strat.initial_multiplier, self.multiplier) 28 | self.assertEqual(self.strat.win_streak, 0) 29 | self.assertEqual(self.strat.lose_streak, 0) 30 | self.assertEqual(self.strat.max_bets, self.max_bets) 31 | self.assertEqual(self.strat.number_of_bets, 0) 32 | self.assertEqual(self.strat.win_count, 0) 33 | self.assertEqual(self.strat.lose_count, 0) 34 | 35 | def test_on_win(self): 36 | self.strat.balance = 1000 # Simulating a win 37 | initial_bet = self.strat.bet 38 | initial_balance = self.strat.balance 39 | self.strat.gamble() # take the bet from the balance 1000 - 10 = 990 40 | self.strat.on_win() 41 | self.assertEqual(self.strat.balance, (initial_balance - self.strat.bet) + initial_bet * self.multiplier) 42 | self.assertEqual(self.strat.bet, self.base_bet) 43 | self.assertEqual(self.strat.win_streak, 1) 44 | self.assertEqual(self.strat.lose_streak, 0) 45 | self.assertEqual(self.strat.win_count, 1) 46 | self.assertEqual(self.strat.lose_count, 0) 47 | 48 | def test_on_lose(self): 49 | self.strat.balance = 900 # Simulating a loss 50 | initial_bet = self.strat.bet 51 | initial_balance = self.strat.balance 52 | self.strat.gamble() # take the bet from the balance 1000 - 10 (base_bet) = 990 53 | self.strat.on_lose() 54 | self.assertEqual(self.strat.balance, initial_balance - initial_bet) 55 | self.assertEqual(self.strat.bet, initial_bet * 2) 56 | self.assertEqual(self.strat.win_streak, 0) 57 | self.assertEqual(self.strat.lose_streak, 1) 58 | self.assertEqual(self.strat.win_count, 0) 59 | self.assertEqual(self.strat.lose_count, 1) 60 | 61 | # Add more tests for other methods as needed 62 | 63 | if __name__ == '__main__': 64 | unittest.main() 65 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AviatorStratChecker 2 | 3 | [![Ruff](https://github.com/luisrx7/AviatorStratChecker/actions/workflows/ruff.yml/badge.svg)](https://github.com/luisrx7/AviatorStratChecker/actions/workflows/ruff.yml) 4 | [![build](https://github.com/luisrx7/AviatorStratChecker/actions/workflows/tests.yml/badge.svg)](https://github.com/luisrx7/AviatorStratChecker/actions/workflows/tests.yml) 5 | 6 | ![AviatorStratChecker Cover](https://github.com/luisrx7/AviatorStratChecker/blob/main/assets/aviator-cover.webp) 7 | 8 | 9 | Welcome to the AviatorStratChecker repository! This project aims to provide a powerful script for efficiently scraping data from the Aviator game on 22bet. By utilizing the scraped data, you can develop and test your own strategies against historical results without the need to wager any real money. We encourage your active participation by opening issues and submitting pull requests to enhance the project. 10 | 11 | ## Features 12 | 13 | - Effortlessly scrape Aviator game data from 22bet. 14 | - Design, simulate, and fine-tune strategies using historical data. 15 | - Automatically bet using your own strategies. 16 | - Collaborate with the community by contributing to the project. 17 | 18 | ## Getting Started 19 | 20 | To set up and use AviatorStratChecker, follow these simple steps: 21 | 22 | 1. **Create a Virtual Environment:** 23 | 24 | ```bash 25 | python -m venv .venv 26 | ``` 27 | 28 | 2. **Activate the virtual environment:** 29 | ```bash 30 | #on Windows 31 | .venv\Scripts\activate 32 | ``` 33 | ```bash 34 | #on Linux 35 | source .venv/bin/activate 36 | ``` 37 | 38 | 39 | 3. **Install required packages:** 40 | ```bash 41 | pip install -r requirements.txt 42 | ``` 43 | 44 | 4. **Create a file named creds.py and add your login credentials:** 45 | ```python 46 | username = "your-email" 47 | password = "your-password" 48 | ``` 49 | 50 | 5. **Install docker and docker-compose:** 51 | - [Docker](https://docs.docker.com/get-docker/) 52 | - [Docker Compose](https://docs.docker.com/compose/install/) 53 | 54 | 55 | 6. **Run Selenium using Docker:** 56 | ```bash 57 | docker-compose up 58 | ``` 59 | 60 | 61 | 7. **Execute the script to start gathering live results:** 62 | ```bash 63 | python scrape.py 64 | ``` 65 | 66 | 67 | 68 | 69 | ## To test Strategies 70 | 71 | 1. **Edit the file strats/custom_strats.py to add your strategies:** 72 | ```python 73 | class ExampleStrat(Strat): 74 | 75 | def on_win(self): 76 | super().on_win() 77 | 78 | self.bet = self.base_bet * 1.5 79 | 80 | def on_lose(self): 81 | super().on_lose() 82 | 83 | self.bet = self.base_bet 84 | ``` 85 | 86 | 2. **Edit the checker.py script to test your new strat** 87 | 88 | 3. **Run the checker.py script to test your strat:** 89 | ```bash 90 | python checker.py 91 | ``` 92 | 93 | 94 | ## After finding a good strategy 95 | 96 | 1. **Edit the file autobet.py to add your strategy:** 97 | ```python 98 | from strats.custom_strats import BestStrat 99 | 100 | def main(): 101 | strat = BestStrat( 102 | description="BestStrat - Example", 103 | start_balance=3000, 104 | base_bet=0.1, 105 | max_bet=0.5, 106 | multiplier=1.9, 107 | max_bets=10000, 108 | ) 109 | ``` 110 | 2. **Run the autobet.py script to start betting:** 111 | ```bash 112 | python autobet.py 113 | ``` 114 | 115 | ## TODO 116 | - Add tests to the existing Strats 117 | - Add tests to the code 118 | - Find some way to scrape past data from the game (maybe using requests) 119 | - Add a way to save the data to a database instead of a csv file 120 | - Add a way to compare strategies 121 | - Add better indicators to the strategies to help the user to decide which one to use 122 | 123 | 124 | ## Contributing 125 | We welcome contributions! If you encounter any issues or have suggestions, please open an issue or submit a pull request. 126 | 127 | 128 | -------------------------------------------------------------------------------- /checker.py: -------------------------------------------------------------------------------- 1 | 2 | from strats.custom_strats import MartingaleStrat, AntiMartingaleStrat, DAlembertStrat, ParoliStrat, one_3_2_6Strat,AntiDAlembertStrat,FibonacciStrat,DAlembertStopLossCooldownStrat 3 | from stratchecker.stratchecker import Strat_checker 4 | 5 | import logging 6 | from rich import print 7 | import pickle 8 | import os 9 | 10 | logging.basicConfig(format='%(message)s', level=logging.INFO, datefmt='%m/%d/%Y %I:%M:%S') 11 | 12 | 13 | 14 | 15 | 16 | start_balance=1000 17 | base_bet=0.1 18 | max_bet=10 19 | multiplier=2 20 | max_bets=1000 21 | runs_per_strat=5 22 | 23 | 24 | 25 | 26 | def read_results(results_filename): 27 | #read file 28 | with open(results_filename) as f: 29 | file_content = f.readlines() 30 | 31 | #remove timestamp and newline 32 | results = [float(x.split(",")[1].replace("\n","")) for x in file_content] 33 | #invert list 34 | results.reverse() 35 | 36 | return results 37 | 38 | def save_slices(slices): 39 | with open('slices.pickle', 'wb') as f: 40 | pickle.dump(slices, f, pickle.HIGHEST_PROTOCOL) 41 | 42 | 43 | def load_slices(): 44 | with open('slices.pickle', 'rb') as f: 45 | return pickle.load(f) 46 | 47 | def main(): 48 | slices = [] 49 | strats = [] 50 | results = read_results("results.txt") 51 | 52 | 53 | #check if slices.pickle exists 54 | #if it does load it 55 | #if it does not create it 56 | if os.path.isfile('slices.pickle'): 57 | slices = load_slices() 58 | 59 | if slices[0][1] - slices[0][0] == max_bets and len(slices) == runs_per_strat: 60 | print("slices are correct") 61 | else: 62 | slices = Strat_checker.slice_results(count=runs_per_strat,max_bets=max_bets,results_length=len(results)) 63 | # slices = Strat_checker.slice_results_with_overlap(count=runs_per_strat,max_bets=max_bets,results_length=len(results),overlap_percent=0.2) 64 | save_slices(slices) 65 | print("slices are incorrect, new slices created") 66 | 67 | 68 | 69 | strats.append(MartingaleStrat("Martingale", 70 | start_balance=start_balance, 71 | base_bet=base_bet, 72 | max_bet=max_bet, 73 | multiplier=multiplier, 74 | max_bets=max_bets )) 75 | 76 | 77 | strats.append(AntiMartingaleStrat("AntiMartingale", 78 | start_balance=start_balance, 79 | base_bet=base_bet, 80 | max_bet=max_bet, 81 | multiplier=multiplier, 82 | max_bets=max_bets )) 83 | 84 | 85 | strats.append(DAlembertStrat(description="DAlembertStrat", 86 | start_balance=start_balance, 87 | base_bet=base_bet, 88 | max_bet=max_bet, 89 | multiplier=multiplier, 90 | max_bets=max_bets, 91 | )) 92 | 93 | strats.append(AntiDAlembertStrat("AntiDAlembertStrat", 94 | start_balance=start_balance, 95 | base_bet=base_bet, 96 | max_bet=max_bet, 97 | multiplier=multiplier, 98 | max_bets=max_bets )) 99 | 100 | 101 | strats.append(DAlembertStopLossCooldownStrat(description="DAlembertStopLossCooldownStrat", 102 | start_balance=start_balance, 103 | base_bet=base_bet, 104 | max_bet=max_bet, 105 | multiplier=multiplier, 106 | max_bets=max_bets, 107 | max_lose_streak = 5, 108 | stop_loss_cooldown = 10 109 | )) 110 | 111 | strats.append(FibonacciStrat("FibonacciStrat", 112 | start_balance=start_balance, 113 | base_bet=base_bet, 114 | max_bet=5, 115 | multiplier=multiplier, 116 | max_bets=max_bets )) 117 | strats.append(ParoliStrat("Paroli", 118 | start_balance=start_balance, 119 | base_bet=base_bet, 120 | max_bet=max_bet, 121 | multiplier=multiplier, 122 | max_bets=max_bets )) 123 | 124 | strats.append(one_3_2_6Strat("one_3_2_6", 125 | start_balance=start_balance, 126 | base_bet=base_bet, 127 | max_bet=max_bet, 128 | multiplier=multiplier, 129 | max_bets=max_bets )) 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | strat_runners = [] 139 | for strat in strats: 140 | strat_runners.append(Strat_checker(strat, results, slices=slices)) 141 | 142 | 143 | for strat_runner in strat_runners: 144 | strat_runner.run(count=runs_per_strat,plot_graph=True) 145 | 146 | 147 | for strat_runner in strat_runners: 148 | strat_runner.report_as_string() 149 | 150 | for strat_runner in strat_runners: 151 | print(strat_runner.report()) 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | if __name__ == "__main__": 162 | main() 163 | 164 | -------------------------------------------------------------------------------- /strats/test_DAlembertStrat.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from strats.custom_strats import DAlembertStrat 3 | 4 | class TestDAlembertStrat(unittest.TestCase): 5 | def setUp(self): 6 | self.start_balance = 1000 7 | self.base_bet = 10 8 | self.max_bet = 100 9 | self.multiplier = 2 10 | self.max_bets = 100 11 | self.strat = DAlembertStrat( 12 | "DAlembert Strategy", 13 | self.start_balance, 14 | self.base_bet, 15 | self.max_bet, 16 | self.multiplier, 17 | self.max_bets 18 | ) 19 | 20 | def test_initial_values(self): 21 | self.assertEqual(self.strat.start_balance, self.start_balance) 22 | self.assertEqual(self.strat.balance, self.start_balance) 23 | self.assertEqual(self.strat.base_bet, self.base_bet) 24 | self.assertEqual(self.strat.bet, self.base_bet) 25 | self.assertEqual(self.strat.max_bet, self.max_bet) 26 | self.assertEqual(self.strat.multiplier, self.multiplier) 27 | self.assertEqual(self.strat.initial_multiplier, self.multiplier) 28 | self.assertEqual(self.strat.win_streak, 0) 29 | self.assertEqual(self.strat.lose_streak, 0) 30 | self.assertEqual(self.strat.max_bets, self.max_bets) 31 | self.assertEqual(self.strat.number_of_bets, 0) 32 | self.assertEqual(self.strat.win_count, 0) 33 | self.assertEqual(self.strat.lose_count, 0) 34 | 35 | def test_on_win(self): 36 | self.strat.balance = 1000 # Simulating a win 37 | initial_bet = self.strat.bet 38 | initial_balance = self.strat.balance 39 | self.strat.gamble() # take the bet from the balance 1000 - 10 = 990 40 | self.strat.on_win() 41 | self.assertEqual(self.strat.balance, (initial_balance - self.strat.bet) + initial_bet * self.multiplier) 42 | self.assertEqual(self.strat.bet, self.base_bet) 43 | self.assertEqual(self.strat.win_streak, 1) 44 | self.assertEqual(self.strat.lose_streak, 0) 45 | self.assertEqual(self.strat.win_count, 1) 46 | self.assertEqual(self.strat.lose_count, 0) 47 | 48 | def test_on_win_in_a_row(self): 49 | #make 2 wins in a row 50 | self.strat.balance = 1000 # Simulating a win 51 | self.strat.start_balance = 1000 52 | self.strat.bet = self.base_bet * 2 # 20 53 | first_bet = self.strat.bet 54 | self.strat.gamble() # take the bet from the balance 1000 - 20 = 990 55 | self.strat.on_win() # 990 + 20 * 2 = 1030 # base bet is 20 and will be reset to 10 56 | self.assertEqual(self.strat.balance, (self.strat.start_balance - first_bet) + first_bet * self.multiplier) 57 | self.assertEqual(self.strat.bet, 10) 58 | self.assertEqual(self.strat.win_streak, 1) 59 | self.assertEqual(self.strat.lose_streak, 0) 60 | self.assertEqual(self.strat.win_count, 1) 61 | self.assertEqual(self.strat.lose_count, 0) 62 | 63 | second_bet = self.strat.bet 64 | balance_before_second_win = self.strat.balance 65 | self.strat.gamble() # take the bet from the balance 1030 - 10 = 1020 66 | self.strat.on_win() # 1020 + 10 * 2 = 1040 67 | 68 | self.assertEqual(self.strat.balance, (balance_before_second_win - second_bet) + second_bet * self.multiplier) 69 | self.assertEqual(self.strat.bet,self.base_bet) 70 | self.assertEqual(self.strat.win_streak, 2) 71 | self.assertEqual(self.strat.lose_streak, 0) 72 | self.assertEqual(self.strat.win_count, 2) 73 | self.assertEqual(self.strat.lose_count, 0) 74 | 75 | 76 | def test_on_lose(self): 77 | self.strat.balance = 900 # Simulating a loss 78 | initial_bet = self.strat.bet 79 | initial_balance = self.strat.balance 80 | self.strat.gamble() # take the bet from the balance 1000 - 10 (base_bet) = 990 81 | self.strat.on_lose() 82 | self.assertEqual(self.strat.balance, initial_balance - initial_bet) 83 | self.assertEqual(self.strat.bet, initial_bet * 2) 84 | self.assertEqual(self.strat.win_streak, 0) 85 | self.assertEqual(self.strat.lose_streak, 1) 86 | self.assertEqual(self.strat.win_count, 0) 87 | self.assertEqual(self.strat.lose_count, 1) 88 | 89 | def test_on_lose_in_a_row(self): 90 | self.strat.balance = 1000 # Simulating a loss 91 | self.strat.bet = self.base_bet # 10 92 | initial_bet = self.strat.bet 93 | initial_balance = self.strat.balance 94 | 95 | self.strat.gamble() # take the bet from the balance 1000 - 10 (base_bet) = 990 96 | self.strat.on_lose() # bet is now 20 97 | 98 | self.assertEqual(self.strat.balance, initial_balance - initial_bet) 99 | self.assertEqual(self.strat.bet, self.strat.base_bet * 2 ) 100 | self.assertEqual(self.strat.win_streak, 0) 101 | self.assertEqual(self.strat.lose_streak, 1) 102 | self.assertEqual(self.strat.win_count, 0) 103 | self.assertEqual(self.strat.lose_count, 1) 104 | 105 | 106 | self.strat.gamble() # take the bet from the balance 990 - 20 = 970 107 | self.strat.on_lose() # bet is now 30 108 | 109 | self.assertEqual(self.strat.bet, self.strat.base_bet * 3 ) 110 | self.assertEqual(self.strat.balance, initial_balance - self.strat.base_bet * 3) 111 | self.assertEqual(self.strat.bet, self.strat.base_bet * 3) 112 | self.assertEqual(self.strat.win_streak, 0) 113 | self.assertEqual(self.strat.lose_streak, 2) 114 | self.assertEqual(self.strat.win_count, 0) 115 | self.assertEqual(self.strat.lose_count, 2) 116 | 117 | 118 | 119 | # Add more tests for other methods as needed 120 | 121 | if __name__ == '__main__': 122 | unittest.main() 123 | -------------------------------------------------------------------------------- /browser/browser.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | 4 | from selenium import webdriver 5 | from webdriver_manager.chrome import ChromeDriverManager 6 | from selenium.webdriver.common.by import By 7 | from selenium.webdriver.support import expected_conditions as EC 8 | from selenium.webdriver.support.wait import WebDriverWait 9 | from selenium.webdriver.common.keys import Keys 10 | 11 | import os 12 | 13 | 14 | class Browser: 15 | def __init__(self,headless = True,test = False,remote_driver = True, 16 | remote_address = "browser", remote_port=4444,use_cookies = False, 17 | profile_path = "", default_timeout = 5): 18 | self.testing = test 19 | self.chrome_options = webdriver.ChromeOptions() 20 | self.use_cookies = use_cookies 21 | self.profile_path = profile_path 22 | self.default_timeout = default_timeout 23 | 24 | 25 | #Chrome options 26 | if sys.platform == "win32": 27 | prefs = {"profile.default_content_settings.popups": 0, 28 | "download.prompt_for_download": False, 29 | "download.directory_upgrade": False} 30 | self.chrome_options.add_experimental_option("prefs",prefs) 31 | 32 | if headless: 33 | self.chrome_options.add_argument("--headless") 34 | # self.chrome_options.add_argument("--window-size=1920,1080") 35 | self.chrome_options.add_argument('--ignore-ssl-errors=yes') 36 | self.chrome_options.add_argument('--ignore-certificate-errors') 37 | self.chrome_options.add_argument('--allow-running-insecure-content') 38 | self.chrome_options.add_argument('--no-sandbox') 39 | self.chrome_options.add_argument("--start-maximized") 40 | self.chrome_options.add_argument('--disable-dev-shm-usage') 41 | self.chrome_options.add_experimental_option('excludeSwitches', 42 | ['enable-logging']) 43 | 44 | # Adding argument to disable the AutomationControlled flag 45 | self.chrome_options.add_argument("--disable-blink-features=AutomationControlled") 46 | 47 | # Exclude the collection of enable-automation switches 48 | self.chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"]) 49 | 50 | # Turn-off userAutomationExtension 51 | self.chrome_options.add_experimental_option("useAutomationExtension", False) 52 | 53 | # Setting the driver path and requesting a page 54 | 55 | 56 | if self.profile_path != "": 57 | print("profile path",self.profile_path) 58 | os.makedirs(self.profile_path, exist_ok=True) 59 | self.chrome_options.add_argument(f"--user-data-dir={self.profile_path}") 60 | # self.chrome_options.add_argument(f"--profile-directory={self.profile_path}") 61 | if remote_driver: 62 | self.driver = webdriver.Remote(command_executor=f'http://{remote_address}:{remote_port}/wd/hub',options=self.chrome_options) 63 | else: 64 | self.driver = webdriver.Chrome(service=ChromeDriverManager().install(), # type: ignore # noqa: E501 65 | options=self.chrome_options) 66 | 67 | # Changing the property of the navigator value for webdriver to undefined 68 | self.driver.execute_script("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})") 69 | 70 | self.driver.implicitly_wait(5) 71 | 72 | def find_elements(self,by, value, timeout=5): 73 | #set timeout 74 | self.driver.implicitly_wait(timeout) 75 | #find elements 76 | elements = self.driver.find_elements(by,value ) 77 | #reset timeout to default 78 | self.driver.implicitly_wait(self.default_timeout) 79 | if len(elements) > 0: 80 | return elements[0] 81 | return None 82 | 83 | def wait_for_element(self,by, element_id, timeout=5): 84 | '''wait for element to be clickable 85 | returns the element if found 86 | ''' 87 | wait = WebDriverWait(self.driver, timeout) 88 | #wait for the bubble to disappear 89 | ret = wait.until(EC.element_to_be_clickable((by, element_id))) 90 | 91 | if ret is not None: 92 | return ret 93 | return None 94 | 95 | 96 | 97 | def close(self): 98 | if hasattr(self, 'driver'): 99 | try: 100 | self.driver.quit() 101 | except Exception: 102 | # console.print_exception(show_locals=True) 103 | pass 104 | 105 | 106 | def __del__(self): 107 | self.close() 108 | 109 | def get_downloads_list(self) -> list: 110 | if not self.driver.current_url.startswith("chrome://downloads"): 111 | self.driver.get("chrome://downloads/") 112 | for i in range(3): 113 | 114 | files = self.driver.execute_script( \ 115 | "return document.querySelector('downloads-manager') " 116 | " .shadowRoot.querySelector('#downloadsList') " 117 | " .items.filter(e => e.state === 'COMPLETE') " 118 | " .map(e => e.filePath || e.file_path || e.fileUrl || e.file_url); " 119 | ) 120 | if len(files) > 0: 121 | return files 122 | return [] 123 | 124 | def execute_script(self,script): 125 | return self.driver.execute_script(script) 126 | 127 | def click_button(self, button_id): 128 | #search for all buttons 129 | buttons = self.driver.find_elements(By.XPATH, button_id) 130 | if len(buttons) > 0: 131 | #click the first button 132 | buttons[0].click() 133 | return True 134 | return False 135 | 136 | def send_keys(self,element_id,keys,clear_first = True): 137 | element = self.find_elements(By.XPATH, element_id,timeout=1) 138 | if element is None: 139 | return False 140 | 141 | if clear_first: 142 | element.send_keys(Keys.CONTROL + "a") 143 | 144 | element.send_keys(keys) 145 | 146 | return True 147 | -------------------------------------------------------------------------------- /strats/strats.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | import logging 4 | from strats.exceptions import MaxBetsReached, InsufficientFunds 5 | 6 | logger = logging.getLogger(__name__) 7 | 8 | 9 | class Strat(): 10 | 11 | def __init__(self,description,start_balance, base_bet, max_bet, multiplier ,max_bets=1000): 12 | self.description = description 13 | self.start_balance = start_balance 14 | self.balance = start_balance 15 | self.base_bet = base_bet 16 | self.bet = base_bet 17 | self.max_bet = max_bet 18 | self.multiplier = multiplier 19 | self.initial_multiplier = multiplier 20 | self.bet_cooldown = 0 #number of bets to skip 21 | 22 | 23 | #stats 24 | self.win_streak = 0 25 | self.lose_streak = 0 26 | self.max_bets = max_bets 27 | self.number_of_bets = 0 28 | self.win_count = 0 29 | self.lose_count = 0 30 | self.lowest_balance = start_balance 31 | self.highest_balance = start_balance 32 | self.highest_win_streak = 0 33 | self.highest_lose_streak = 0 34 | self.skipped_bets = 0 35 | self.bet_distribution = {} 36 | self.balance_history = [] 37 | 38 | 39 | 40 | 41 | def describe(self): 42 | #return a string describing the strat and all of its parameters can be multiline 43 | return f"""{self.description} 44 | start balance: {self.start_balance} 45 | base bet: {self.base_bet} 46 | max bet: {self.max_bet} 47 | multiplier: {self.multiplier} 48 | max bets: {self.max_bets} 49 | """ 50 | 51 | 52 | def calculate_bet(self, result): 53 | ''' 54 | result represents where the plane crashed 55 | if the plane crashed below the multiplier, it is a loss 56 | if the plane crashed above the multiplier, it is a win 57 | ''' 58 | 59 | logging.info(f"result: {result}, multiplier: {self.multiplier}, bet: {self.bet}, balance: {self.balance:.2f}, win streak: {self.win_streak}, lose streak: {self.lose_streak}") 60 | if self.multiplier < result: 61 | self.on_win() 62 | else: 63 | self.on_lose() 64 | 65 | self.update_stats() 66 | 67 | def gamble(self): 68 | #take the bet from the balance 69 | logging.debug(f"\nbet {self.bet} - {self.multiplier} from balance {self.balance:.2f}") 70 | 71 | if self.number_of_bets >= self.max_bets: 72 | logging.error(f"number of bets allowed {self.max_bets} reached, not betting anymore") 73 | self.bet = 0 74 | self.multiplier = 0 75 | raise MaxBetsReached 76 | if self.bet > self.balance: 77 | logging.info(f"bet {self.bet} is greater than balance {self.balance:.2f} cannot bet anymore") 78 | self.bet = 0 79 | self.multiplier = 0 80 | raise InsufficientFunds 81 | if self.bet > self.max_bet: 82 | logging.info(f"bet {self.bet} is greater than max bet {self.max_bet} using max bet {self.max_bet} instead") 83 | self.bet = self.max_bet 84 | 85 | if self.bet_cooldown > 0: 86 | self.bet_cooldown -= 1 87 | logging.info(f"bet cooldown is active, skipping bet, {self.bet_cooldown} bets left") 88 | self.bet = 0 89 | self.multiplier = 0 90 | self.skipped_bets += 1 91 | return 92 | 93 | self.multiplier = self.initial_multiplier 94 | self.balance = self.balance - self.bet 95 | self.number_of_bets += 1 96 | 97 | # Update the bet distribution dictionary with the current bet amount 98 | if self.bet in self.bet_distribution: 99 | self.bet_distribution[self.bet] += 1 100 | else: 101 | self.bet_distribution[self.bet] = 1 102 | 103 | #update the balance history 104 | self.balance_history.append(self.balance) 105 | 106 | 107 | def update_stats(self): 108 | if self.balance > self.highest_balance: 109 | self.highest_balance = self.balance 110 | if self.balance < self.lowest_balance: 111 | self.lowest_balance = self.balance 112 | if self.win_streak > self.highest_win_streak: 113 | self.highest_win_streak = self.win_streak 114 | if self.lose_streak > self.highest_lose_streak: 115 | self.highest_lose_streak = self.lose_streak 116 | 117 | 118 | 119 | def on_win(self): 120 | #bet manipulation should be implemented in subclass 121 | logging.info(f"win : previous bet: {self.bet}, previous balance: {self.balance:.2f}, new balance: {self.balance + (self.bet * self.multiplier):.2f}") 122 | self.balance += self.bet * self.multiplier 123 | self.win_streak += 1 124 | self.lose_streak = 0 125 | self.win_count += 1 126 | 127 | 128 | def on_lose(self): 129 | #bet manipulation should be implemented in subclass 130 | logging.warning(f"lose : previous bet: {self.bet}, previous balance: {self.balance:.2f}, new balance: {self.balance - self.bet:.2f}") 131 | self.win_streak = 0 132 | self.lose_streak += 1 133 | self.lose_count += 1 134 | 135 | 136 | def reset(self): 137 | 138 | logging.info(f"game start : base bet: {self.bet}, balance: {self.balance:.2f}") 139 | self.bet = self.base_bet 140 | self.win_streak = 0 141 | self.lose_streak = 0 142 | self.number_of_bets = 0 143 | self.multiplier = self.initial_multiplier 144 | self.win_count = 0 145 | self.lose_count = 0 146 | self.balance = self.start_balance 147 | self.lowest_balance = self.start_balance 148 | self.highest_balance = self.start_balance 149 | self.highest_win_streak = 0 150 | self.highest_lose_streak = 0 151 | self.bet_distribution = {} 152 | self.balance_history = [] 153 | 154 | 155 | 156 | def report_as_string(self): 157 | # print the final balance 158 | return(f""" 159 | start balance: {self.start_balance} 160 | final balance: {self.balance:.2f} 161 | won: {self.balance - self.start_balance:.2f} 162 | win count: {self.win_count} 163 | lose count: {self.lose_count} 164 | bet count: {self.number_of_bets} 165 | base bet: {self.base_bet} 166 | max bet: {self.max_bet} 167 | multiplier: {self.multiplier} 168 | lowest balance: {self.lowest_balance:.2f} 169 | highest balance: {self.highest_balance:.2f} 170 | highest win streak: {self.highest_win_streak} 171 | highest lose streak: {self.highest_lose_streak} 172 | skipped bets: {self.skipped_bets} 173 | """) 174 | 175 | def report(self): 176 | return {"start_balance": self.start_balance, 177 | "final_balance": self.balance, 178 | "won": self.balance - self.start_balance, 179 | "win_count": self.win_count, 180 | "lose_count": self.lose_count, 181 | "bet_count": self.number_of_bets, 182 | "base_bet": self.base_bet, 183 | "max_bet": self.max_bet, 184 | "multiplier": self.multiplier, 185 | "lowest_balance": self.lowest_balance, 186 | "highest_balance": self.highest_balance, 187 | "highest_win_streak": self.highest_win_streak, 188 | "highest_lose_streak": self.highest_lose_streak, 189 | "skipped_bets": self.skipped_bets, 190 | 191 | } 192 | 193 | 194 | 195 | 196 | -------------------------------------------------------------------------------- /stratchecker/stratchecker.py: -------------------------------------------------------------------------------- 1 | from strats.strats import Strat 2 | from strats.exceptions import MaxBetsReached, InsufficientFunds 3 | 4 | import logging 5 | import random 6 | from rich import print 7 | 8 | import matplotlib.pyplot as plt 9 | 10 | logger = logging.getLogger(__name__) 11 | 12 | 13 | 14 | class Strat_checker(): 15 | def __init__(self, strat:Strat, results, slices=[]) -> None: 16 | self.strat = strat 17 | self.slices = slices 18 | self.strat_reports_as_string = [] 19 | self.strat_reports = [] 20 | 21 | self.results = results 22 | 23 | 24 | 25 | @staticmethod 26 | def slice_results_with_overlap(count, max_bets, results_length, overlap_percent): 27 | slices = [] 28 | min_overlap = int(max_bets * overlap_percent) 29 | 30 | for i in range(count): 31 | if i == 0: 32 | start_index = random.randint(0, int(results_length / 2)) 33 | else: 34 | previous_start, previous_end = slices[i - 1] 35 | potential_start = previous_end - min_overlap 36 | start_index = random.randint(min(previous_start, potential_start), results_length - max_bets) 37 | 38 | end_index = start_index + max_bets 39 | slices.append((start_index, end_index)) 40 | 41 | return slices 42 | 43 | @staticmethod 44 | def slice_results(count,max_bets,results_length): 45 | 46 | ''' 47 | slice results in a random place making sure that 48 | the max_length of the slice is not exceeded 49 | return the slice indexes 50 | #TODO 51 | a good slice is one that contains a loss streak of at least 6 losses assuming a 52 | multiplier of 2 53 | based on the assumption that the strat will recover from that loss streak 54 | 55 | 56 | ''' 57 | slices = [] 58 | for i in range(count): 59 | #get random start index 60 | start_index = random.randint(0,results_length-max_bets) 61 | 62 | #get random end index 63 | end_index = start_index+max_bets 64 | 65 | slices.append((start_index,end_index)) 66 | 67 | return slices 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | def report_as_string(self): 76 | print(f"\nStrategy: {self.strat.description}") 77 | for report in self.strat_reports_as_string: 78 | print(report) 79 | 80 | def report(self): 81 | avg_won = 0 82 | avg_final_balance = 0 83 | avg_highest_balance = 0 84 | avg_lowest_balance = 0 85 | 86 | 87 | runs_with_negative_balance = 0 88 | runs_backrupt = 0 89 | 90 | for report in self.strat_reports: 91 | avg_won += report["won"] 92 | avg_final_balance += report["final_balance"] 93 | avg_highest_balance += report["highest_balance"] 94 | avg_lowest_balance += report["lowest_balance"] 95 | 96 | if report["final_balance"] < report["start_balance"]: 97 | runs_with_negative_balance += 1 98 | if report["final_balance"] <= report["base_bet"]: 99 | runs_backrupt += 1 100 | 101 | 102 | avg_won = avg_won / len(self.strat_reports) 103 | avg_final_balance = avg_final_balance / len(self.strat_reports) 104 | avg_highest_balance = avg_highest_balance / len(self.strat_reports) 105 | avg_lowest_balance = avg_lowest_balance / len(self.strat_reports) 106 | 107 | return { 108 | "strat_name": self.strat.description, 109 | "avg_won": avg_won, 110 | "avg_final_balance": avg_final_balance, 111 | "avg_highest_balance": avg_highest_balance, 112 | "avg_lowest_balance": avg_lowest_balance, 113 | "runs_with_negative_balance": runs_with_negative_balance, 114 | "runs_backrupt": runs_backrupt, 115 | 116 | } 117 | @staticmethod 118 | def filter_n_elements(arr,n): 119 | result = [x for x in arr if x % n == 0] 120 | return result 121 | 122 | def plot_balance_history(self,clear:bool=False,run_number:int=-1): 123 | color = ["blue","red","green","yellow","black","orange","purple","pink","brown","gray"] 124 | 125 | upper_bound = self.slices[run_number][1] if len(self.strat.balance_history) == self.slices[run_number][1] else self.slices[run_number][0] + len(self.strat.balance_history) 126 | x_axis = range(self.slices[run_number][0],upper_bound ) 127 | 128 | #on the first plot use balance_history , on the second use bet_distribution 129 | # Initialise the subplot function using number of rows and columns 130 | figure, axis = plt.subplots(1, 2) 131 | 132 | axis[0].plot(x_axis,self.strat.balance_history,color=color[run_number]) 133 | axis[0].set_ylabel('balance') 134 | axis[0].set_xlabel('bet number') 135 | axis[0].set_title(f"{self.strat.description} - run {run_number+1}\nslice: {self.slices[run_number]}") 136 | 137 | 138 | axis[1].bar(list(self.strat.bet_distribution.keys()), 139 | list(self.strat.bet_distribution.values()), 140 | width=round(self.strat.base_bet/2,2), 141 | ec="black" ) 142 | axis[1].set_xlabel("Bet Amount") 143 | axis[1].set_ylabel("Frequency") 144 | axis[1].set_title(f"Distribution of Bets - run {run_number+1}") 145 | 146 | # # Adding text labels on top of each bar 147 | for bet, freq in self.strat.bet_distribution.items(): 148 | axis[1].text(bet, freq, str(freq), ha="center", va="bottom") 149 | 150 | plt.show() 151 | 152 | #clear the plot 153 | if clear: 154 | plt.clf() 155 | 156 | 157 | def get_slices(self): 158 | return self.slices 159 | 160 | def set_slices(self,slices): 161 | self.slices = slices 162 | 163 | 164 | def run(self,count:int=1,random_slice:bool=True,plot_graph:bool=False): 165 | ''' 166 | run the strat count times 167 | if random_slice is True, slice the results in a random places 168 | count must be greater than 1 if random_slice is True 169 | ''' 170 | 171 | self.strat.balance_history = [] 172 | 173 | 174 | if random_slice is True and count == 1: 175 | logging.warning("random_slice is True and count is 1, setting random_slice to False") 176 | random_slice = False 177 | 178 | if self.strat.max_bets > len(self.results): 179 | logging.warning(f"max bets {self.strat.max_bets} is greater than the number of results {len(self.results)}, setting max bets to {len(self.results)}") 180 | self.strat.max_bets = len(self.results) 181 | 182 | 183 | if len(self.slices) == 0: 184 | #if no slices are defined, generate count slices 185 | self.slices = self.slice_results(self.strat.max_bets,count) 186 | 187 | 188 | for i in range(count): 189 | 190 | self.strat.reset() 191 | try: 192 | for result in self.results[self.slices[i][0]:self.slices[i][1]]: 193 | self.strat.gamble() 194 | self.strat.calculate_bet(result) 195 | 196 | 197 | except MaxBetsReached or InsufficientFunds: 198 | logger.info(f"max bets reached or insufficient funds strat: {self.strat.description} - run {i+1}") 199 | pass 200 | 201 | except Exception as e: 202 | logging.error(e) 203 | 204 | finally: 205 | self.strat_reports_as_string.append(self.strat.report_as_string() + f"slice: {self.slices[i]}") 206 | self.strat_reports.append(self.strat.report()) 207 | 208 | if plot_graph: 209 | self.plot_balance_history(run_number=i) 210 | 211 | 212 | 213 | 214 | 215 | 216 | -------------------------------------------------------------------------------- /strats/custom_strats.py: -------------------------------------------------------------------------------- 1 | from strats.strats import Strat 2 | 3 | 4 | 5 | class MartingaleStrat(Strat): 6 | 7 | def __init__(self,description,start_balance, base_bet, max_bet, multiplier ,max_bets): 8 | super().__init__(description,start_balance, base_bet, max_bet, multiplier ,max_bets) 9 | 10 | 11 | def on_win(self): 12 | super().on_win() 13 | 14 | self.bet = self.base_bet 15 | 16 | def on_lose(self): 17 | super().on_lose() 18 | 19 | self.bet = self.bet * 2 20 | 21 | 22 | class AntiMartingaleStrat(Strat): 23 | ''' 24 | Reverse Martingale 25 | One compelling alternative is the Reverse Martingale, also known as 26 | the Anti-Martingale. As opposed to doubling your bet amount after a 27 | loss at online casinos, the Reverse Martingale instructs you to 28 | double your bet amount after a win. When you lose, you go back to 29 | the start and bet 1 unit again. 30 | 31 | This system is designed to capitalize on winning streaks. However, 32 | all of your previous profits can be wiped out with a single loss, 33 | so this system is best used in short bursts. 34 | ''' 35 | 36 | def __init__(self,description,start_balance, base_bet, max_bet, multiplier ,max_bets): 37 | super().__init__(description,start_balance, base_bet, max_bet, multiplier ,max_bets) 38 | 39 | def on_win(self): 40 | super().on_win() 41 | 42 | self.bet = self.bet * 2 43 | 44 | def on_lose(self): 45 | super().on_lose() 46 | 47 | self.bet = self.base_bet 48 | 49 | 50 | class DAlembertStrat(Strat): 51 | ''' 52 | D Alembert System | Increase your bet amount by 1 unit whenever you lose and decrease by 1 unit if you win. 53 | 1 unit = base_bet 54 | ''' 55 | def __init__(self,description,start_balance, base_bet, max_bet, multiplier ,max_bets): 56 | super().__init__(description,start_balance, base_bet, max_bet, multiplier ,max_bets) 57 | if description == "": 58 | self.description = f"DAlembert {base_bet} {multiplier}" 59 | 60 | def on_win(self): 61 | super().on_win() 62 | if self.bet > self.base_bet: 63 | self.bet = round(self.bet - self.base_bet,2) 64 | else: 65 | self.bet = self.base_bet 66 | 67 | def on_lose(self): 68 | super().on_lose() 69 | 70 | if self.bet < self.max_bet: 71 | self.bet = round(self.bet + self.base_bet,2) 72 | else: 73 | self.bet = self.max_bet 74 | 75 | 76 | class AntiDAlembertStrat(Strat): 77 | ''' 78 | Anti D Alembert System | Increase your bet amount by 1 unit whenever you win and decrease by 1 unit if you lose. 79 | 1 unit = base_bet 80 | ''' 81 | def __init__(self,description,start_balance, base_bet, max_bet, multiplier ,max_bets): 82 | super().__init__(description,start_balance, base_bet, max_bet, multiplier ,max_bets) 83 | if description == "": 84 | self.description = f"AntiDAlembert {base_bet} {multiplier}" 85 | 86 | def on_lose(self): 87 | super().on_lose() 88 | if self.bet > self.base_bet: 89 | self.bet = round(self.bet - self.base_bet,2) 90 | else: 91 | self.bet = self.base_bet 92 | 93 | 94 | 95 | def on_win(self): 96 | super().on_win() 97 | 98 | if self.bet < self.max_bet: 99 | self.bet = round(self.bet + self.base_bet,2) 100 | else: 101 | self.bet = self.max_bet 102 | 103 | class DAlembertStopLossCooldownStrat(Strat): 104 | ''' 105 | D Alembert System | Increase your bet amount by 1 unit whenever you lose and decrease by 1 unit if you win. 106 | if you lose x times in a row, enter a cooldown period of y bets 107 | 1 unit = base_bet 108 | ''' 109 | def __init__(self,description,start_balance, base_bet, max_bet, multiplier ,max_bets, max_lose_streak, stop_loss_cooldown): 110 | super().__init__(description,start_balance, base_bet, max_bet, multiplier ,max_bets) 111 | if description == "": 112 | self.description = f"DAlembert Stop Loss Cooldown {max_lose_streak} {stop_loss_cooldown}" 113 | 114 | self.max_lose_streak = max_lose_streak 115 | self.stop_loss_cooldown = stop_loss_cooldown 116 | 117 | 118 | def on_win(self): 119 | super().on_win() 120 | 121 | if self.bet < self.max_bet: 122 | self.bet = round(self.bet + self.base_bet,2) 123 | else: 124 | self.bet = self.max_bet 125 | 126 | def on_lose(self): 127 | super().on_lose() 128 | 129 | if self.lose_streak >= self.max_lose_streak: 130 | self.bet_cooldown = self.stop_loss_cooldown 131 | 132 | else: 133 | if self.bet > self.base_bet: 134 | self.bet = round(self.bet - self.base_bet,2) 135 | else: 136 | self.bet = self.base_bet 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | class ParoliStrat(Strat): 145 | ''' 146 | Paroli System | Increase your bet amount by 1 unit whenever you win. If you win three times in a row, go back to the start and bet 1 unit again. 1 unit = base_bet 147 | ''' 148 | def __init__(self,description,start_balance, base_bet, max_bet, multiplier ,max_bets): 149 | super().__init__(description,start_balance, base_bet, max_bet, multiplier ,max_bets) 150 | 151 | 152 | def on_win(self): 153 | super().on_win() 154 | 155 | if self.win_streak == 3: 156 | self.bet = self.base_bet 157 | else: 158 | self.bet = self.bet + self.base_bet 159 | 160 | def on_lose(self): 161 | super().on_lose() 162 | 163 | self.bet = self.base_bet 164 | 165 | class OscarGrindStrat(Strat): 166 | ''' 167 | Oscar Grind | Increase your bet amount by 1 unit after each win. Keep it the same when you lose. When you end up with at least 1 unit of profit, go back to the start and bet 1 unit again. 168 | 1 unit = base_bet 169 | 170 | needs work 171 | ''' 172 | 173 | def __init__(self,description,start_balance, base_bet, max_bet, multiplier ,max_bets): 174 | super().__init__(description,start_balance, base_bet, max_bet, multiplier ,max_bets) 175 | 176 | 177 | def on_win(self): 178 | super().on_win() 179 | 180 | self.bet = self.bet + self.base_bet 181 | 182 | def on_lose(self): 183 | super().on_lose() 184 | 185 | self.bet = self.base_bet 186 | 187 | class FibonacciStrat(Strat): 188 | ''' 189 | Fibonacci | Increase your bet amount by the sum of the previous two bets after each loss. When you win, go back two numbers in the sequence and bet that amount. 190 | 1 unit = base_bet 191 | 192 | needs work 193 | 194 | ''' 195 | 196 | 197 | def __init__(self,description,start_balance, base_bet, max_bet, multiplier ,max_bets): 198 | super().__init__(description,start_balance, base_bet, max_bet, multiplier ,max_bets) 199 | self.fibonacci_sequence = [1,1,2,3,5,8,13,21,34,55,89,144,233,377,610] 200 | self.current_fibonacci_index = 0 201 | 202 | def on_win(self): 203 | super().on_win() 204 | #go down the fibonacci sequence by 2 205 | self.current_fibonacci_index -= 2 if self.current_fibonacci_index >= 2 else 0 206 | self.bet = self.bet + self.base_bet 207 | 208 | def on_lose(self): 209 | super().on_lose() 210 | #go up the fibonacci sequence 211 | self.current_fibonacci_index += 1 if self.current_fibonacci_index < len(self.fibonacci_sequence) else len(self.fibonacci_sequence) 212 | self.bet = self.base_bet * self.fibonacci_sequence[self.current_fibonacci_index] 213 | 214 | 215 | class one_3_2_6Strat(Strat): 216 | ''' 217 | 1-3-2-6 System | Start by betting 1 unit, followed by 3 units, 2 units, and 6 units, before starting again, but only move up the sequence when you win. 218 | 1 unit = base_bet 219 | 220 | not sure if this is implemented correctly 221 | ''' 222 | 223 | def __init__(self,description,start_balance, base_bet, max_bet, multiplier ,max_bets): 224 | super().__init__(description,start_balance, base_bet, max_bet, multiplier ,max_bets) 225 | self.bet_sequence = [1,3,2,6] 226 | 227 | def on_win(self): 228 | super().on_win() 229 | self.bet = self.bet_sequence[self.win_streak % len(self.bet_sequence)] * self.base_bet 230 | 231 | def on_lose(self): 232 | super().on_lose() 233 | 234 | self.bet = self.base_bet -------------------------------------------------------------------------------- /aviator/aviator.py: -------------------------------------------------------------------------------- 1 | from browser.browser import Browser 2 | import helium 3 | import aviator.vars as vars 4 | import creds as creds 5 | from selenium.webdriver.common.by import By 6 | import time 7 | from datetime import datetime 8 | 9 | from strats.strats import Strat 10 | 11 | from selenium.webdriver.support import expected_conditions as EC 12 | from selenium.webdriver.support.wait import WebDriverWait 13 | from selenium.common.exceptions import StaleElementReferenceException, NoSuchElementException 14 | 15 | import logging 16 | 17 | logger = logging.getLogger(__name__) 18 | 19 | class Aviator(Browser): 20 | ''' 21 | this class will interact with the browser 22 | 23 | ''' 24 | 25 | def __init__(self, headless=False, test=False, remote_driver=True, 26 | remote_address="127.0.0.1",remote_port=4446, use_cookies=False, 27 | debug=False,strat: Strat = None): 28 | super().__init__(headless= headless, test=test, remote_driver=remote_driver, 29 | remote_address=remote_address,remote_port=remote_port, 30 | use_cookies=use_cookies, profile_path=vars.profile_path) 31 | 32 | self.debug = debug 33 | self.strat = strat 34 | 35 | if self.strat is not None: 36 | self.strat.reset() 37 | 38 | helium.set_driver(self.driver) 39 | 40 | 41 | 42 | def login(self): 43 | helium.go_to("https://22bet-b.com") 44 | if not self.logged_in(): 45 | helium.click("LOG IN") 46 | helium.write(creds.username, into="ID or Email") 47 | helium.write(creds.password, into="Password") 48 | helium.click("Remember") 49 | helium.click("LOG IN") 50 | input("press enter to continue") 51 | 52 | 53 | 54 | def logged_in(self): 55 | if self.debug: 56 | logger.debug("checking if logged in") 57 | element = helium.S("#user-money") 58 | if element.exists(): 59 | if self.debug: 60 | logger.debug("logged in") 61 | return True 62 | else: 63 | if self.debug: 64 | logger.debug("not logged in") 65 | return False 66 | 67 | 68 | def in_game(self): 69 | ''' 70 | check if we are in game 71 | ''' 72 | if self.debug: 73 | logger.debug("checking if in game") 74 | 75 | element = self.find_elements(By.XPATH, vars.game_name, timeout=0.5) 76 | if element or self.driver.title == "Aviator": 77 | if self.debug: 78 | logger.debug("in game") 79 | return True 80 | 81 | if self.debug: 82 | logger.debug("not in game") 83 | return False 84 | 85 | def get_last_game_result(self): 86 | ''' 87 | get last game result 88 | ''' 89 | # if self.debug: 90 | # logger.debug("getting last game result") 91 | 92 | element = self.find_elements(By.XPATH, vars.last_game_result, timeout=1) 93 | 94 | if element is not None: 95 | return element.text.strip().replace("x", "") 96 | 97 | 98 | results = self.get_game_results() 99 | if len(results) > 0: 100 | return results[0] 101 | 102 | def process_bet(self, result): 103 | ''' 104 | check if a strat is defined 105 | if it is, use it to calculate the next bet 106 | if not warn the user that a strat is not defined 107 | ''' 108 | if self.debug: 109 | logger.debug(f"processing bet for result {result}") 110 | if self.strat is None: 111 | logger.debug("WARNING: no strat defined") 112 | return False 113 | 114 | self.strat.calculate_bet(result) 115 | if self.debug: 116 | logger.debug(f"bet: {self.strat.bet}, multiplier: {self.strat.multiplier}") 117 | 118 | if self.strat.bet == 0 or self.strat.multiplier == 0: 119 | if self.debug: 120 | logger.debug("bet or multiplier is 0, not placing bet") 121 | return False 122 | 123 | if self.place_bet(self.strat.bet, self.strat.multiplier) is False: 124 | if self.debug: 125 | logger.debug("could not place bet") 126 | return False 127 | 128 | self.strat.gamble() 129 | 130 | 131 | def get_game_results(self): 132 | ''' 133 | get game result 134 | ''' 135 | 136 | 137 | # Find the div element with class "payouts-block" 138 | payouts_div = self.find_elements(By.CLASS_NAME, "payouts-block") 139 | if payouts_div is None: 140 | if self.debug: 141 | logger.debug("could not get game results") 142 | return [] 143 | 144 | # Find all elements with class "bubble-multiplier" within the payouts div 145 | multiplier_elements = payouts_div.find_elements(By.CLASS_NAME, "bubble-multiplier") 146 | 147 | results = [] 148 | # Extract the values 149 | try: 150 | for element in multiplier_elements: 151 | results.append(element.text.strip().replace("x", "")) 152 | except StaleElementReferenceException or NoSuchElementException: 153 | #refresh the page 154 | self.driver.refresh() 155 | pass 156 | 157 | 158 | 159 | 160 | 161 | if len(results) > 0: 162 | # if self.debug: 163 | # logger.debug("got game results") 164 | return results 165 | else: 166 | if self.debug: 167 | logger.debug("could not get game results") 168 | 169 | 170 | return [] 171 | 172 | 173 | def get_balance(self): 174 | ''' 175 | get balance 176 | ''' 177 | if self.debug: 178 | logger.debug("getting balance") 179 | element = self.find_elements(By.XPATH, vars.balance) 180 | #element is a span with the balance 181 | if element: 182 | if self.debug: 183 | logger.debug("got balance") 184 | return element.text 185 | else: 186 | if self.debug: 187 | logger.debug("could not get balance") 188 | return None 189 | 190 | 191 | def disconnected(self): 192 | ''' 193 | check if disconnected warning is displayed 194 | ''' 195 | disconnected_element = self.find_elements(By.XPATH, vars.disconnected_warning, timeout=0.5) 196 | if disconnected_element is not None: 197 | return True 198 | return False 199 | 200 | def wait_for_game_to_finish(self): 201 | ''' 202 | wait for game to finish 203 | since the game is in a loop, we need to wait for the game to finish 204 | the only way to check if the game is finished is to check if we have 205 | a new result different from the last one 206 | ''' 207 | 208 | if self.debug: 209 | logger.debug("waiting for game to finish") 210 | last_result = self.get_last_game_result() 211 | while True: 212 | if self.disconnected(): 213 | if self.debug: 214 | logger.debug("disconnected") 215 | break 216 | result = self.get_last_game_result() 217 | if result is not None: 218 | if result != last_result : 219 | break 220 | if self.debug: 221 | logger.debug(".", end="") 222 | time.sleep(0.1) 223 | if self.debug: 224 | logger.debug("\ngame finished") 225 | 226 | def add_to_log(self, result): 227 | ''' 228 | add result to results.txt in this 229 | format timestamp (format dd-mm-yyyy hh:mm:ss),result 230 | ''' 231 | if self.debug: 232 | logger.debug(f"adding result {result} to log") 233 | with open("results.txt", "a") as f: 234 | f.write(f"{datetime.now().strftime('%d-%m-%Y %H:%M:%S')},{result}\n") 235 | 236 | 237 | def setup_auto_bet(self): 238 | ''' 239 | click the buttons to setup auto cashout 240 | ''' 241 | if self.debug: 242 | logger.debug("setting up auto bet") 243 | 244 | if self.click_button(vars.bet_type_button) is False: 245 | if self.debug: 246 | logger.debug("could not click bet type button") 247 | return False 248 | if self.click_button(vars.auto_cashout_button) is False: 249 | if self.debug: 250 | logger.debug("could not click auto cashout button") 251 | return False 252 | 253 | 254 | def place_bet(self,amount, multiplier): 255 | ''' 256 | place bet with amount and multiplier 257 | ''' 258 | if self.debug: 259 | logger.debug(f"setting bet amount to {amount} at multiplier {multiplier}") 260 | 261 | 262 | if self.send_keys(vars.bet_amount_input_box, str(amount)) is False: 263 | if self.debug: 264 | logger.debug("could not set bet amount") 265 | return False 266 | 267 | if self.send_keys(vars.multiplier_input_box, str(multiplier)) is False: 268 | if self.debug: 269 | logger.debug("could not set multiplier") 270 | return False 271 | 272 | if self.click_button(vars.place_bet_button) is False: 273 | if self.debug: 274 | logger.debug("could not click place bet button") 275 | return False 276 | 277 | return True 278 | 279 | 280 | def go_to_game(self): 281 | wait = WebDriverWait(self.driver, 10) 282 | 283 | helium.go_to("https://22bet-b.com/slots") 284 | helium.write("AVIATOR", into="SEARCH") 285 | #sleep for 2 seconds to let the search results load 286 | time.sleep(2) 287 | self.click_button(vars.play_free_button_of_first_search_result) 288 | 289 | #wait for the game to load 290 | time.sleep(2) 291 | 292 | # Store the ID of the original window 293 | original_window = self.driver.current_window_handle 294 | 295 | # Check we don't have other windows open already 296 | assert len(self.driver.window_handles) == 1 297 | 298 | # Click the link which opens in a new window 299 | self.click_button(vars.open_new_window_button) 300 | 301 | # Wait for the new window or tab 302 | wait.until(EC.number_of_windows_to_be(2)) 303 | 304 | # Loop through until we find a new window handle 305 | for window_handle in self.driver.window_handles: 306 | if window_handle != original_window: 307 | self.driver.switch_to.window(window_handle) 308 | break 309 | 310 | # Wait for the new tab to finish loading content 311 | wait.until(EC.title_is("Aviator")) 312 | 313 | #maximize window 314 | self.driver.maximize_window() 315 | 316 | #wait for the game to open in a new window 317 | time.sleep(2) 318 | 319 | 320 | 321 | --------------------------------------------------------------------------------