Procházet zdrojové kódy

ArbChecker needs more odds

Axel Nordh před 3 roky
rodič
revize
8b4c88ccbe

+ 1 - 17
Odds/.classpath

@@ -10,26 +10,10 @@
 			<attribute name="maven.pomderived" value="true"/>
 		</attributes>
 	</classpathentry>
-	<classpathentry kind="src" path="target/generated-sources/annotations">
+	<classpathentry including="**/*.java" kind="src" output="target/classes" path="src">
 		<attributes>
 			<attribute name="optional" value="true"/>
 			<attribute name="maven.pomderived" value="true"/>
-			<attribute name="ignore_optional_problems" value="true"/>
-			<attribute name="m2e-apt" value="true"/>
-		</attributes>
-	</classpathentry>
-	<classpathentry kind="src" output="target/classes" path="src">
-		<attributes>
-			<attribute name="optional" value="true"/>
-			<attribute name="maven.pomderived" value="true"/>
-		</attributes>
-	</classpathentry>
-	<classpathentry kind="src" output="target/test-classes" path="target/generated-test-sources/test-annotations">
-		<attributes>
-			<attribute name="optional" value="true"/>
-			<attribute name="maven.pomderived" value="true"/>
-			<attribute name="ignore_optional_problems" value="true"/>
-			<attribute name="m2e-apt" value="true"/>
 		</attributes>
 	</classpathentry>
 	<classpathentry kind="output" path="target/classes"/>

+ 33 - 0
Odds/Expekt.csv

@@ -0,0 +1,33 @@
+FC Oleksandriya,Rukh Vynnyky,1.04,9.5,46.0
+Johor Darul Takzim,Borussia Dortmund,801.0,301.0,-1.0
+Sydkorea,Ghana,2.7,3.15,2.95
+Brasilien,Schweiz,1.47,4.5,8.0
+Portugal,Uruguay,2.08,3.35,4.3
+Châteauroux,Le Mans,2.15,3.4,2.85
+UD Ibiza,FC Andorra,3.05,2.9,2.48
+Qarabağ,Zira IK,1.11,6.4,12.5
+Aris Limassol,Pafos FC,2.55,3.0,2.48
+Olympiakos Nicosia,Nea Salamis Famagusta,2.63,2.95,2.48
+Punjab,Aizawl FC,1.68,3.7,4.1
+Csikszereda Miercurea Ciuc,CSC Șelimbăr,1.34,4.1,8.0
+Ayr United,Pollok,1.18,6.5,12.0
+Altinordu,Manisa Büyükşehir Belediyespor,3.15,3.15,2.1
+Wakiso Giants FC,Express FC,1.85,3.0,4.2
+Ecuador,Senegal,2.45,3.25,3.25
+Nederländerna,Qatar,1.21,7.0,17.0
+Iran,USA,4.1,3.55,2.0
+Wales,England,8.5,4.75,1.43
+ASO Chlef,MC EL Bayadh,1.98,2.9,4.1
+Belouizdad,CS Constantine,1.55,3.6,5.8
+MC Alger,Paradou Ac,1.5,3.5,7.5
+NC Magra,USM Alger,4.1,2.85,2.02
+RC Arbaâ,USM Khenchela,2.18,2.65,3.85
+US Biskra,JS Kabylie,2.38,2.6,3.5
+ES Sétif,MC Oran,1.5,3.65,6.5
+JS Saoura,AB Chelghoum Laid,1.11,6.75,20.0
+Kenkre,Neroca FC,3.65,3.05,1.95
+Chindia Târgovişte,Universitatea Craiova,4.1,3.15,1.95
+ND Gorica,Tabor Sezana,2.08,3.2,3.0
+Maribor,NK Bravo,1.7,3.4,4.0
+SV Türkgücü-Ataspor München,SV Heimstetten,1.47,4.25,5.1
+BSG Chemie Leipzig,BFC Dynamo,2.05,3.5,3.0

+ 42 - 0
Odds/NordicBet.csv

@@ -0,0 +1,42 @@
+Johor Darul Ta zim,Borussia Dortmund,51.0,51.0,-1.0
+PAOK,FC Ashdod,1.85,4.0,4.33
+Real Betis (Perapanamera),Lazio (Rodja),1.01,20.0,25.0
+West Ham (Taz),Real Sociedad (Jekos),8.55,3.63,1.45
+Sydkorea,Ghana,2.7,3.15,2.85
+Ierapetra,Proodeftiki,1.7,4.0,4.2
+Kifisia,Kallithea,2.35,2.9,3.2
+Brasilien,Schweiz,1.48,4.4,7.3
+Altinordu,Manisa FK,3.25,3.4,2.2
+Portugal,Uruguay,2.0,3.3,4.2
+ArzignanoChiampo,L.R. Vicenza Virtus,3.4,3.2,2.1
+Ayr United,Pollok,1.15,7.0,15.0
+Hitchin Town,Redditch Utd,2.75,3.4,2.25
+Kings Langley,Nuneaton,2.95,3.3,2.2
+Shandong Luneng,Cangzhou Mighty Lions,1.22,6.5,10.0
+Tianjin Teda,Guangzhou R F,1.6,4.0,5.0
+Hebei China Fortune Football Club,Dalian Aerbin FC,67.0,13.0,1.02
+Shanghai SIPG,Henan Construction,1.75,3.6,4.5
+Wuhan Three Towns,Changchun Yatai,1.28,5.5,9.0
+Zhejiang Professional,Beijing Guoan,2.55,3.5,2.55
+Shenzhen XiangXue,Wuhan Zall,1.68,4.0,4.5
+Pordenone,Pro Patria,1.8,3.3,4.5
+Foggia,Messina,1.7,3.5,4.75
+Tunisien,Frankrike,6.8,4.1,1.55
+Australien,Danmark,7.0,4.2,1.52
+Catanzaro,Giugliano,1.38,4.5,7.5
+Feralpisalo,Juventus-U23,2.2,3.1,3.3
+Lecco,Renate,2.25,3.2,3.1
+Mantova,Albinoleffe,2.4,2.95,3.1
+Pergolettese,Novara,2.7,3.1,2.6
+Piacenza,Triestina,2.7,3.1,2.6
+Virtus Verona,Pro Sesto,2.55,3.0,2.8
+Latina,Monopoli,2.35,3.0,3.1
+Taranto,Crotone,4.2,3.5,1.8
+Polen,Argentina,7.7,4.35,1.47
+Saudiarabien,Mexiko,4.95,4.05,1.68
+Sangiuliano City,Padova,2.55,3.0,2.8
+Avellino,Juve Stabia,2.2,3.1,3.3
+Potenza,Audace Cerignola,2.6,3.0,2.75
+Turris,Fidelis Andria,2.15,3.2,3.3
+Francavilla,Pescara,3.75,3.5,1.87
+Sporting CP,Farense,1.15,7.0,17.0

+ 23 - 15
Odds/pom.xml

@@ -43,16 +43,11 @@
   </repository>
 </repositories>
   <dependencies>
-    <dependency>
-      <groupId>com.google.guava</groupId>
-      <artifactId>guava</artifactId>
-      <version>30.0-jre</version>
-    </dependency>
-    <dependency>
-      <groupId>net.sourceforge.htmlunit</groupId>
-      <artifactId>htmlunit</artifactId>
-      <version>2.66.0</version>
-    </dependency>
+	<dependency>
+	    <groupId>net.sourceforge.htmlunit</groupId>
+	    <artifactId>htmlunit</artifactId>
+	    <version>2.66.0</version>
+	</dependency>
     <dependency>
       <groupId>com.github.jbytecode</groupId>
       <artifactId>RCaller</artifactId>
@@ -68,10 +63,23 @@
         <artifactId>spring-boot-maven-plugin</artifactId>
         <version>2.7.1</version>
     </dependency>
-<dependency>
-    <groupId>com.fasterxml.jackson.core</groupId>
-    <artifactId>jackson-databind</artifactId>
-    <version>2.13.3</version>
-</dependency>
+	<dependency>
+	    <groupId>com.fasterxml.jackson.core</groupId>
+	    <artifactId>jackson-databind</artifactId>
+	    <version>2.13.3</version>
+	</dependency>
+		<!-- https://mvnrepository.com/artifact/org.seleniumhq.selenium/selenium-java -->
+	<dependency>
+	    <groupId>org.seleniumhq.selenium</groupId>
+	    <artifactId>selenium-java</artifactId>
+	    <version>4.6.0</version>
+	</dependency>
+		<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
+	<dependency>
+	    <groupId>com.google.guava</groupId>
+	    <artifactId>guava</artifactId>
+	    <version>31.1-jre</version>
+	</dependency>
   </dependencies>
+  
 </project>

+ 50 - 48
Odds/src/Main.java

@@ -2,57 +2,59 @@ import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStreamReader;
 
-import parser.ExpektParser;
+import main.ArbChecker;
 import parser.OddsPortal;
 
 public class Main {
 
-	public static void main(String[] args) {
-
-		//testExpektParser();
-		updateFromOddsportal();
-		// op.getHistoricMatches("soccer", "japan", "j2-league", "2020");
-		// callingRScript();
-	}
-
-	private static void testExpektParser() {
-		ExpektParser expektParser = new ExpektParser();
-
-	}
-
-	private static void updateFromOddsportal() {
-		final OddsPortal op = new OddsPortal();
-			System.out.println("Getting Yesterdays matches");
-			op.getYesterdaysMatches();
-			System.out.println("Getting Todays Matches");
-			op.getTodaysMatches();
-			System.out.println("Getting tomorrows matches");
-			op.getTomorrowsMatches();
-
-			op.getNextDaysMatches();
-	}
-
-	private static void callingRScript() throws IOException {
-		System.out.println(System.getProperty("user.dir"));
-		final String rPath = System.getProperty("user.dir") + "\\src\\RScript\\scorePrediction.R";
-
-		final Process exec = Runtime.getRuntime().exec("C:\\Program Files\\R\\R-3.6.3\\bin\\Rscript.exe " + rPath);
-
-		final BufferedReader stdInput = new BufferedReader(new InputStreamReader(exec.getInputStream()));
-
-		final BufferedReader stdError = new BufferedReader(new InputStreamReader(exec.getErrorStream()));
-
-		// Read the output from the command
-		System.out.println("Here is the standard output of the command:\n");
-		String s = null;
-		while ((s = stdInput.readLine()) != null) {
-			System.out.println(s);
-		}
-		// Read any errors from the attempted command
-		System.out.println("Here is the standard error of the command (if any):\n");
-		while ((s = stdError.readLine()) != null) {
-			System.out.println(s);
-		}
-	}
+    public static void main(String[] args) {
+
+        // testExpektParser();
+//        updateFromOddsportal();
+
+        testArbMatches();
+//        op.getHistoricMatches("soccer", "japan", "j2-league", "2020");
+        // callingRScript();
+    }
+
+    private static void testArbMatches() {
+        new ArbChecker();
+    }
+
+    private static void updateFromOddsportal() {
+        final OddsPortal op = new OddsPortal();
+        System.out.println("Getting Yesterdays matches");
+        op.getYesterdaysMatches();
+        System.out.println("Getting Todays Matches");
+        op.getTodaysMatches();
+        System.out.println("Getting tomorrows matches");
+        op.getTomorrowsMatches();
+
+        System.out.println("Getting next matches");
+        op.getNextDaysMatches();
+    }
+
+    private static void callingRScript() throws IOException {
+        System.out.println(System.getProperty("user.dir"));
+        final String rPath = System.getProperty("user.dir") + "\\src\\RScript\\scorePrediction.R";
+
+        final Process exec = Runtime.getRuntime().exec("C:\\Program Files\\R\\R-3.6.3\\bin\\Rscript.exe " + rPath);
+
+        final BufferedReader stdInput = new BufferedReader(new InputStreamReader(exec.getInputStream()));
+
+        final BufferedReader stdError = new BufferedReader(new InputStreamReader(exec.getErrorStream()));
+
+        // Read the output from the command
+        System.out.println("Here is the standard output of the command:\n");
+        String s = null;
+        while ((s = stdInput.readLine()) != null) {
+            System.out.println(s);
+        }
+        // Read any errors from the attempted command
+        System.out.println("Here is the standard error of the command (if any):\n");
+        while ((s = stdError.readLine()) != null) {
+            System.out.println(s);
+        }
+    }
 
 }

+ 120 - 0
Odds/src/main/ArbChecker.java

@@ -0,0 +1,120 @@
+package main;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.AbstractMap.SimpleEntry;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import object.ArbResults;
+
+public class ArbChecker {
+
+    private List<ArbResults> nordicBetMatches = new ArrayList<>();
+    private List<ArbResults> expektResults = new ArrayList<>();
+
+    public ArbChecker() {
+//        NordicBetParser nbp = new NordicBetParser();
+//        nordicBetMatches.addAll(nbp.parseSoccerMatches().stream().map(m -> new ArbResults(m, "NordicBet")).toList());
+//        printResultsToArrayFile(nordicBetMatches, "NordicBet.csv");
+//
+//        ExpektParser ep = new ExpektParser();
+//        expektResults.addAll(ep.seleniumParseSoccerMatches().stream().map(m -> new ArbResults(m, "Expekt")).toList());
+//        printResultsToArrayFile(expektResults, "Expekt.csv");
+
+        getResultsFromFile("NordicBet.csv", nordicBetMatches, "NordicBet");
+        getResultsFromFile("Expekt.csv", expektResults, "Expekt");
+
+        List<SimpleEntry<String, List<ArbResults>>> resultLists = new ArrayList<>();
+        SimpleEntry<String, List<ArbResults>> entryNordicBet = new SimpleEntry<>("NordicBet", this.nordicBetMatches);
+        resultLists.add(entryNordicBet);
+        checkForArbs(new SimpleEntry<>("Expekt", expektResults), resultLists);
+    }
+
+    private void checkForArbs(SimpleEntry<String, List<ArbResults>> mainList,
+            List<SimpleEntry<String, List<ArbResults>>> resultLists) {
+        for (ArbResults match : mainList.getValue()) {
+            String replaceParentecesRegEx = "\\(.*\\)| [A-Za-z]{2} |Club|club";
+            List<String> homeTeamParts = Arrays
+                    .asList(match.getHomeTeam().replaceAll(replaceParentecesRegEx, "").trim().split(" "));
+            List<String> awayTeamParts = Arrays
+                    .asList(match.getAwayTeam().replaceAll(replaceParentecesRegEx, "").trim().split(" "));
+
+            for (SimpleEntry<String, List<ArbResults>> list : resultLists) {
+                List<ArbResults> homeTeamFound = list.getValue().stream()
+                        .filter(p -> Arrays.asList(p.getHomeTeam().replaceAll(replaceParentecesRegEx, "")
+                                .trim().split(" ")).stream().anyMatch(homeTeamParts::contains))
+                        .toList();
+
+                List<ArbResults> awayTeamFound = list.getValue().stream()
+                        .filter(p -> Arrays.asList(p.getAwayTeam().replaceAll(replaceParentecesRegEx, "")
+                                .trim().split(" ")).stream().anyMatch(awayTeamParts::contains))
+                        .toList();
+
+                if (homeTeamFound.isEmpty() || awayTeamFound.isEmpty()) {
+//                    System.out.println(match.getHomeTeam() + "-" + match.getAwayTeam() + " Not found in list");
+                    continue;
+                }
+
+                if (homeTeamFound.size() == 1) {
+                    match.addOdds1(homeTeamFound.get(0).getOdds1());
+                    match.addOddsX(homeTeamFound.get(0).getOddsX());
+                    match.addOdds2(homeTeamFound.get(0).getOdds2());
+                    System.out.println(
+                            "Found match mainlist " + match.toStringWithExtra() + " arb value "
+                                    + calculateArbValue3Part(match));
+                } else if (awayTeamFound.size() == 1) {
+                    match.addOdds1(awayTeamFound.get(0).getOdds1());
+                    match.addOddsX(awayTeamFound.get(0).getOddsX());
+                    match.addOdds2(awayTeamFound.get(0).getOdds2());
+                    System.out.println(
+                            "Found match mainlist " + match.toStringWithExtra() + " arb value "
+                                    + calculateArbValue3Part(match));
+                }
+            }
+        }
+    }
+
+    private float calculateArbValue3Part(ArbResults res) {
+        return (1 / res.getMaxOdds1()) + (1 / res.getMaxOddsX()) + (1 / res.getMaxOdds2());
+    }
+
+    private void printResultsToArrayFile(List<ArbResults> results, String fileName) {
+        try (BufferedWriter bw = new BufferedWriter(
+                new FileWriter(System.getProperty("user.dir") + File.separator + fileName))) {
+            StringBuilder sb = new StringBuilder();
+
+            for (ArbResults r : results) {
+                sb.append(r.toString());
+                sb.append(System.lineSeparator());
+            }
+
+            bw.write(sb.toString());
+        } catch (IOException e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        }
+    }
+
+    private void getResultsFromFile(String fileName, List<ArbResults> resultsList, String bookie) {
+        try (BufferedReader br = new BufferedReader(
+                new FileReader(System.getProperty("user.dir") + File.separator + fileName))) {
+
+            String line;
+            while ((line = br.readLine()) != null) {
+                String[] values = line.split(",");
+                resultsList.add(new ArbResults(values[0], values[1], Float.parseFloat(values[2]),
+                        Float.parseFloat(values[3]),
+                        Float.parseFloat(values[4]), bookie));
+            }
+        } catch (IOException e) {
+            // TODO Auto-generated catch block
+            e.printStackTrace();
+        }
+    }
+}

binární
Odds/src/main/resources/chromedriver.exe


+ 67 - 0
Odds/src/object/ArbResults.java

@@ -0,0 +1,67 @@
+package object;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class ArbResults extends ResultDTO {
+
+    List<Odds> odds1List = new ArrayList<>();
+    List<Odds> oddsXList = new ArrayList<>();
+    List<Odds> odds2List = new ArrayList<>();
+
+    String bookie;
+
+    public ArbResults(String homeTeamName, String awayTeamName, float odds1, float oddsX, float odds2, String bookie) {
+        super(homeTeamName, awayTeamName, odds1, oddsX, odds2);
+
+        odds1List.add(new Odds(bookie, odds1));
+        oddsXList.add(new Odds(bookie, oddsX));
+        odds2List.add(new Odds(bookie, odds2));
+        this.bookie = bookie;
+    }
+
+    public ArbResults(ResultDTO m, String bookie) {
+        this(m.homeTeam, m.awayTeam, m.odds1, m.oddsX, m.odds2, bookie);
+    }
+
+    public void addOdds1(float value) {
+        odds1List.add(new Odds(bookie, value));
+    }
+
+    public void addOddsX(float value) {
+        oddsXList.add(new Odds(bookie, value));
+    }
+
+    public void addOdds2(float value) {
+        odds2List.add(new Odds(bookie, value));
+    }
+
+    public float getMaxOdds1() {
+        return Collections.max(odds1List.stream().map(m -> m.odds).toList());
+    }
+
+    public float getMaxOddsX() {
+        return Collections.max(oddsXList.stream().map(m -> m.odds).toList());
+    }
+
+    public float getMaxOdds2() {
+        return Collections.max(odds2List.stream().map(m -> m.odds).toList());
+    }
+
+    public String toStringWithExtra() {
+        String string = super.toString();
+        string += "(" + getMaxOdds1() + "," + getMaxOddsX() + "," + getMaxOdds2() + ")";
+        return string;
+    }
+
+    class Odds {
+        String bookie;
+        float odds;
+
+        public Odds(String bookie, float odds) {
+            this.bookie = bookie;
+            this.odds = odds;
+        }
+    }
+}

+ 158 - 143
Odds/src/object/ResultDTO.java

@@ -4,151 +4,166 @@ import java.time.LocalDateTime;
 
 public class ResultDTO {
 
-	String tableName;
-	LocalDateTime gameDate;
-	String homeTeam;
-	String awayTeam;
-	int homeScore;
-	int awayScore;
-	boolean overtime;
-	float odds1;
-	float oddsX;
-	float odds2;
-	int countryId;
-	String season;
-	int leagueId;
-	int sportId;
-
-	public ResultDTO(String tableName, LocalDateTime gameDate, String homeTeam, String awayTeam, int homeScore,
-			int awayScore, boolean overtime, float odds1, float oddsX, float odds2, int countryId, String season,
-			int leagueId, int sportId) {
-		super();
-		this.tableName = tableName;
-		this.gameDate = gameDate;
-		this.homeTeam = homeTeam;
-		this.awayTeam = awayTeam;
-		this.homeScore = homeScore;
-		this.awayScore = awayScore;
-		this.overtime = overtime;
-		this.odds1 = odds1;
-		this.oddsX = oddsX;
-		this.odds2 = odds2;
-		this.countryId = countryId;
-		this.season = season;
-		this.leagueId = leagueId;
-		this.sportId = sportId;
-	}
-
-	public String getTableName() {
-		return tableName;
-	}
-
-	public void setTableName(String tableName) {
-		this.tableName = tableName;
-	}
-
-	public LocalDateTime getGameDate() {
-		return gameDate;
-	}
-
-	public void setGameDate(LocalDateTime gameDate) {
-		this.gameDate = gameDate;
-	}
-
-	public String getHomeTeam() {
-		return homeTeam;
-	}
-
-	public void setHomeTeam(String homeTeam) {
-		this.homeTeam = homeTeam;
-	}
-
-	public String getAwayTeam() {
-		return awayTeam;
-	}
-
-	public void setAwayTeam(String awayTeam) {
-		this.awayTeam = awayTeam;
-	}
-
-	public int getHomeScore() {
-		return homeScore;
-	}
-
-	public void setHomeScore(int homeScore) {
-		this.homeScore = homeScore;
-	}
-
-	public int getAwayScore() {
-		return awayScore;
-	}
-
-	public void setAwayScore(int awayScore) {
-		this.awayScore = awayScore;
-	}
-
-	public boolean isOvertime() {
-		return overtime;
-	}
-
-	public void setOvertime(boolean overtime) {
-		this.overtime = overtime;
-	}
-
-	public float getOdds1() {
-		return odds1;
-	}
-
-	public void setOdds1(float odds1) {
-		this.odds1 = odds1;
-	}
-
-	public float getOddsX() {
-		return oddsX;
-	}
-
-	public void setOddsX(float oddsX) {
-		this.oddsX = oddsX;
-	}
-
-	public float getOdds2() {
-		return odds2;
-	}
-
-	public void setOdds2(float odds2) {
-		this.odds2 = odds2;
-	}
-
-	public int getCountryId() {
-		return countryId;
-	}
-
-	public void setCountryId(int countryId) {
-		this.countryId = countryId;
-	}
-
-	public String getSeason() {
-		return season;
-	}
-
-	public void setSeason(String season) {
-		this.season = season;
-	}
+    String tableName;
+    LocalDateTime gameDate;
+    String homeTeam;
+    String awayTeam;
+    int homeScore;
+    int awayScore;
+    boolean overtime;
+    float odds1;
+    float oddsX;
+    float odds2;
+    int countryId;
+    String season;
+    int leagueId;
+    int sportId;
+
+    public ResultDTO() {
+    };
+
+    public ResultDTO(String tableName, LocalDateTime gameDate, String homeTeam, String awayTeam, int homeScore,
+            int awayScore, boolean overtime, float odds1, float oddsX, float odds2, int countryId, String season,
+            int leagueId, int sportId) {
+        super();
+        this.tableName = tableName;
+        this.gameDate = gameDate;
+        this.homeTeam = homeTeam;
+        this.awayTeam = awayTeam;
+        this.homeScore = homeScore;
+        this.awayScore = awayScore;
+        this.overtime = overtime;
+        this.odds1 = odds1;
+        this.oddsX = oddsX;
+        this.odds2 = odds2;
+        this.countryId = countryId;
+        this.season = season;
+        this.leagueId = leagueId;
+        this.sportId = sportId;
+    }
+
+    public ResultDTO(String homeTeamName, String awayTeamName, float odds1, float oddsX, float odds2) {
+        this.homeTeam = homeTeamName;
+        this.awayTeam = awayTeamName;
+        this.odds1 = odds1;
+        this.oddsX = oddsX;
+        this.odds2 = odds2;
+    }
+
+    public String getTableName() {
+        return tableName;
+    }
+
+    public void setTableName(String tableName) {
+        this.tableName = tableName;
+    }
+
+    public LocalDateTime getGameDate() {
+        return gameDate;
+    }
+
+    public void setGameDate(LocalDateTime gameDate) {
+        this.gameDate = gameDate;
+    }
+
+    public String getHomeTeam() {
+        return homeTeam;
+    }
+
+    public void setHomeTeam(String homeTeam) {
+        this.homeTeam = homeTeam;
+    }
+
+    public String getAwayTeam() {
+        return awayTeam;
+    }
+
+    public void setAwayTeam(String awayTeam) {
+        this.awayTeam = awayTeam;
+    }
+
+    public int getHomeScore() {
+        return homeScore;
+    }
+
+    public void setHomeScore(int homeScore) {
+        this.homeScore = homeScore;
+    }
+
+    public int getAwayScore() {
+        return awayScore;
+    }
+
+    public void setAwayScore(int awayScore) {
+        this.awayScore = awayScore;
+    }
+
+    public boolean isOvertime() {
+        return overtime;
+    }
+
+    public void setOvertime(boolean overtime) {
+        this.overtime = overtime;
+    }
+
+    public float getOdds1() {
+        return odds1;
+    }
+
+    public void setOdds1(float odds1) {
+        this.odds1 = odds1;
+    }
+
+    public float getOddsX() {
+        return oddsX;
+    }
+
+    public void setOddsX(float oddsX) {
+        this.oddsX = oddsX;
+    }
+
+    public float getOdds2() {
+        return odds2;
+    }
+
+    public void setOdds2(float odds2) {
+        this.odds2 = odds2;
+    }
+
+    public int getCountryId() {
+        return countryId;
+    }
+
+    public void setCountryId(int countryId) {
+        this.countryId = countryId;
+    }
+
+    public String getSeason() {
+        return season;
+    }
+
+    public void setSeason(String season) {
+        this.season = season;
+    }
+
+    public int getLeagueId() {
+        return leagueId;
+    }
 
-	public int getLeagueId() {
-		return leagueId;
-	}
+    public void setLeagueId(int leagueId) {
+        this.leagueId = leagueId;
+    }
 
-	public void setLeagueId(int leagueId) {
-		this.leagueId = leagueId;
-	}
+    public int getSportId() {
+        return sportId;
+    }
 
-	public int getSportId() {
-		return sportId;
-	}
-
-	public void setSportId(int sportId) {
-		this.sportId = sportId;
-	}
+    public void setSportId(int sportId) {
+        this.sportId = sportId;
+    }
 
+    @Override
+    public String toString() {
+        return homeTeam + "," + awayTeam + "," + odds1 + "," + oddsX + "," + odds2;
+    }
 }

+ 134 - 32
Odds/src/parser/ExpektParser.java

@@ -2,30 +2,35 @@ package parser;
 
 import java.io.IOException;
 import java.net.URL;
-import java.nio.charset.Charset;
+import java.time.Duration;
 import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
-import javax.sql.rowset.spi.SyncResolver;
-
-import org.apache.commons.io.IOUtils;
+import org.openqa.selenium.By;
+import org.openqa.selenium.JavascriptExecutor;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.chrome.ChromeDriver;
+import org.openqa.selenium.support.ui.ExpectedConditions;
+import org.openqa.selenium.support.ui.WebDriverWait;
 
 import com.fasterxml.jackson.databind.JsonNode;
 import com.fasterxml.jackson.databind.ObjectMapper;
-import com.gargoylesoftware.htmlunit.BrowserVersion;
 import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException;
-import com.gargoylesoftware.htmlunit.Page;
 import com.gargoylesoftware.htmlunit.WebClient;
 import com.gargoylesoftware.htmlunit.WebResponse;
 import com.gargoylesoftware.htmlunit.html.HtmlPage;
 
+import object.ResultDTO;
+
+public class ExpektParser extends ParserBase {
+
+    List<ResultDTO> result = new ArrayList<>();
 
-public class ExpektParser {
-    
     public ExpektParser() {
-        getJSON();
-        //getSoccerMatches();
+        // getSoccerMatches();
     }
 
     public static JsonNode get(URL url) throws Exception {
@@ -33,19 +38,115 @@ public class ExpektParser {
         return mapper.readTree(url);
     }
 
+    public List<ResultDTO> seleniumParseSoccerMatches() {
+        List<ResultDTO> result = new ArrayList<>();
+        String url = "https://www.expekt.se/sv-se/sports#filter/football";
+
+        ParserBase pb = new ParserBase();
+        ChromeDriver driver = pb.getSeleniumDriver();
+        driver.get(url);
+
+        JavascriptExecutor js = driver;
+
+        WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(30));
+
+        wait.until(ExpectedConditions
+                .numberOfElementsToBeMoreThan(By.xpath("//div[contains(@class, 'KambiBC-betoffer-labels')]"), 0));
+
+        List<WebElement> headers = driver.findElements(By.xpath(
+                "//div[contains(@class,'CollapsibleContainer__CollapsibleWrapper') and not(contains(@class, 'expanded'))]"));
+
+        for (WebElement header : headers) {
+            WebElement title = header.findElement(By.xpath(".//div[contains(@class,'CollapsibleContainer__Title')]"));
+            scrollElementIntoViewCenter(driver, header);
+            if (title.getText().contains("Imorgon")) {
+                header.click();
+                scrollElementIntoViewCenter(driver, title);
+            }
+        }
+
+        List<WebElement> expandableDivs = driver
+                .findElements(By.xpath("//div[contains(@class, 'KambiBC-betoffer-labels')]"));
+
+        scrollElementIntoViewCenter(driver, expandableDivs.get(0));
+        for (WebElement element : expandableDivs) {
+            if (!checkIfElementExists(element, ".//following-sibling::ul")) {
+                element.click();
+                scrollElementIntoView(driver, element);
+            }
+        }
+
+        wait.until(ExpectedConditions.numberOfElementsToBeMoreThan(
+                By.xpath("//ul[contains(@class, 'KambiBC-list-view__column KambiBC-list-view__event-list')]"),
+                expandableDivs.size() - 3));
+
+        List<WebElement> leagueLists = driver
+                .findElements(
+                        By.xpath("//ul[contains(@class, 'KambiBC-list-view__column KambiBC-list-view__event-list')]"));
+
+        List<WebElement> matches = driver.findElements(By.xpath("//div[@class='KambiBC-event-item__event-wrapper']"));
+
+        for (WebElement element : matches) {
+
+            List<WebElement> participants = element
+                    .findElements(By.xpath(".//div[@class='KambiBC-event-participants__name']"));
+            if (participants.size() != 2) {
+                continue;
+            }
+            String homeTeamName = participants.get(0).getText();
+            String awayTeamName = participants.get(1).getText();
+
+            if (!checkIfElementExists(element,
+                    ".//div[@class='KambiBC-list-view__column KambiBC-bet-offers-list__column KambiBC-bet-offers-list__column--num-3']")) {
+                continue;
+            }
+            WebElement oddsContainer = element.findElement(By.xpath(
+                    ".//div[@class='KambiBC-list-view__column KambiBC-bet-offers-list__column KambiBC-bet-offers-list__column--num-3']"));
+
+            List<WebElement> odds = oddsContainer
+                    .findElements(By.xpath(".//div[@class='OutcomeButton__Odds-sc-lxwzc0-6 iYOCD']"));
+
+            int od = 0;
+            float odds1 = -1f;
+            float oddsX = -1f;
+            float odds2 = -1f;
+            for (WebElement o : odds) {
+                if (od == 0) {
+                    odds1 = Float.valueOf(o.getText());
+                } else if (od == 1) {
+                    oddsX = Float.valueOf(o.getText());
+                } else if (od == 2) {
+                    odds2 = Float.valueOf(o.getText());
+                }
+                od++;
+            }
+
+            result.add(new ResultDTO(homeTeamName, awayTeamName, odds1, oddsX, odds2));
+            System.out.println(homeTeamName + "-" + awayTeamName + " (" + odds1 + "," + oddsX + "," + odds2 + ")");
+        }
+
+        driver.close();
+        return result;
+    }
+
+    // EXPEKT
     // https://eu-offering.kambicdn.org/offering/v2018/expektse/listView/football.json?lang=sv_SE&market=SE
 
-private void getJSON() {
-    try {
-        URL url = new URL("https://eu-offering.kambicdn.org/offering/v2018/expektse/listView/football.json?lang=sv_SE&market=SE");
-        System.out.println("Getting url");
-        JsonNode node = get(url);
-        System.out.println("Got URL");
-        System.out.println(node.elements().next());
-    } catch (Exception e) {
-        e.printStackTrace();
+    // UNIBET
+    // https://eu-offering.kambicdn.org/offering/v2018/ubse/listView/football.json?lang=sv_SE&market=SE
+
+    public void getJSON() {
+        try {
+            URL url = new URL(
+                    "https://eu-offering.kambicdn.org/offering/v2018/expektse/listView/football.json?lang=sv_SE&market=SE");
+            System.out.println("Getting url");
+            JsonNode node = get(url);
+            System.out.println("Got URL");
+            System.out.println(node.elements().next());
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
     }
-}
 
     private void getSoccerMatches() {
         String url = "https://www.expekt.se/sv-se/sports#filter/football";
@@ -57,31 +158,32 @@ private void getJSON() {
         webClient.getOptions().setThrowExceptionOnScriptError(false);
         Logger.getLogger("com.gargoylesoftware").setLevel(Level.OFF);
 
-        //webClient.waitForBackgroundJavaScript(20000);
+        // webClient.waitForBackgroundJavaScript(20000);
         try {
             System.out.println("Starting to Get page with wait 20 seconds " + LocalDateTime.now());
 
             webClient.waitForBackgroundJavaScript(3000);
-            HtmlPage page = webClient.getPage(url);  
-            
+            HtmlPage page = webClient.getPage(url);
+
             page = webClient.getPage(url);
 
             System.out.println(page.asNormalizedText());
 
             WebResponse webResponse = page.getWebResponse();
             int i = 0;
-            // while (!webResponse.getContentAsString().contains("KambiBC-event-groups-list")){
-            //     webClient.waitForBackgroundJavaScript(500);
-            //     if (i++ < 50) {
-            //         System.out.println("Waiting");
-
-            //     }  else {
-            //         break;
-            //     }
+            // while
+            // (!webResponse.getContentAsString().contains("KambiBC-event-groups-list")){
+            // webClient.waitForBackgroundJavaScript(500);
+            // if (i++ < 50) {
+            // System.out.println("Waiting");
+
+            // } else {
+            // break;
+            // }
             // }
             System.out.println("Done waiting " + LocalDateTime.now());
 
-            //System.out.println(webResponse.getContentAsString());
+            // System.out.println(webResponse.getContentAsString());
         } catch (FailingHttpStatusCodeException | IOException e) {
             // TODO Auto-generated catch block
             e.printStackTrace();

+ 169 - 0
Odds/src/parser/NordicBetParser.java

@@ -0,0 +1,169 @@
+package parser;
+
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+import org.openqa.selenium.By;
+import org.openqa.selenium.JavascriptExecutor;
+import org.openqa.selenium.StaleElementReferenceException;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.chrome.ChromeDriver;
+import org.openqa.selenium.support.ui.ExpectedConditions;
+import org.openqa.selenium.support.ui.WebDriverWait;
+
+import com.gargoylesoftware.htmlunit.ElementNotFoundException;
+
+import object.ResultDTO;
+
+public class NordicBetParser extends ParserBase {
+
+    List<ResultDTO> parsedMatches = new ArrayList<>();
+
+    public List<ResultDTO> parseSoccerMatches() {
+        String url = "https://d-cf.ndbplayground.net/stc--187551415/stc--187551415/fotboll";
+        ChromeDriver driver = getSeleniumDriver();
+
+        driver.manage().deleteAllCookies();
+        driver.get(url);
+        JavascriptExecutor js = driver;
+        WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(30));
+        wait.until(ExpectedConditions
+                .numberOfElementsToBeMoreThan(By.xpath("//div[contains(@class,'obg-event-row-event-container')]"), 0));
+
+        List<WebElement> matchDivs = driver
+                .findElements(By.xpath("//div[contains(@class,'obg-event-row-event-container')]"));
+
+        List<WebElement> expandableDivs = driver
+                .findElements(By.xpath(
+                        "//div[contains(@class, 'obg-m-events-master-detail-header') and not(contains(@class, 'expanded'))]"));
+
+        // leta upp child span med text "kommande idag" och kanske "imorgon"
+        // Behöver också kolla om det finns mer att ladda obg-show-more-less-button
+        // ng-star-inserted
+        // Kontrollera att det inte står "Show less" då den minimerar igen
+        for (WebElement element : expandableDivs) {
+            try {
+                String xpath = ".//span[text()='Live']";
+                if (checkIfElementExists(element, xpath)) {
+                    element.click();
+                    wait.until(ExpectedConditions.numberOfElementsToBeMoreThan(By.xpath(
+                            "//div[contains(@class,'obg-event-row-event-container')]"), matchDivs.size()));
+                    matchDivs = driver
+                            .findElements(By.xpath("//div[contains(@class,'obg-event-row-event-container')]"));
+                    scrollElementIntoView(driver, element);
+                }
+
+                xpath = ".//span[text()='Kommande idag']";
+                if (checkIfElementExists(element, xpath)) {
+                    element.click();
+                    wait.until(ExpectedConditions.numberOfElementsToBeMoreThan(By.xpath(
+                            "//div[contains(@class,'obg-event-row-event-container')]"), matchDivs.size()));
+
+                    matchDivs = driver
+                            .findElements(By.xpath("//div[contains(@class,'obg-event-row-event-container')]"));
+                    scrollElementIntoView(driver, element);
+
+                }
+
+                xpath = ".//span[text()='Imorgon']";
+                if (checkIfElementExists(element, xpath)) {
+                    element.click();
+                    wait.until(ExpectedConditions.numberOfElementsToBeMoreThan(By.xpath(
+                            "//div[contains(@class,'obg-event-row-event-container')]"), matchDivs.size()));
+
+                    matchDivs = driver
+                            .findElements(By.xpath("//div[contains(@class,'obg-event-row-event-container')]"));
+                    scrollElementIntoView(driver, element);
+                }
+
+            } catch (ElementNotFoundException e) {
+                // Empty by design, no more matches today
+            }
+        }
+
+        expandAllMatches(driver, wait, matchDivs);
+
+        matchDivs = driver
+                .findElements(By.xpath("//div[contains(@class,'obg-event-row-event-container')]"));
+
+        js.executeScript("window.stop;");
+        for (WebElement match : matchDivs) {
+            parseMatch(driver, match);
+        }
+        System.out.println("found " + matchDivs.size() + " matches");
+
+        driver.close();
+        return parsedMatches;
+    }
+
+    private void expandAllMatches(ChromeDriver driver, WebDriverWait wait, List<WebElement> matchDivs) {
+        List<WebElement> buttons = driver
+                .findElements(By.xpath("//button[contains(@class,'obg-show-more-less-button')]"));
+
+        for (WebElement button : buttons) {
+            WebElement findElement = button.findElement(By.xpath(".//span"));
+            while (!findElement.getText().contains("less")) {
+                button.click();
+                findElement = button.findElement(By.xpath(".//span"));
+            }
+
+        }
+
+        if (buttons.size() > 0) {
+            wait.until(ExpectedConditions.numberOfElementsToBeMoreThan(By.xpath(
+                    "//div[contains(@class,'obg-event-row-event-container')]"), matchDivs.size()));
+        }
+    }
+
+    private void parseMatch(ChromeDriver driver, WebElement match) {
+        // teams div, two div children with hometeam awayTeam
+        // "class obg-event-info-participant obg-event-info-participant-default-layout"
+        try {
+            List<WebElement> participants = match
+                    .findElements(By.xpath(".//div[@class='obg-event-info-participant-label ng-star-inserted']"));
+
+            String homeTeamName = participants.get(0).getAttribute("title");
+            String awayTeamName = participants.get(1).getAttribute("title");
+
+            List<WebElement> oddsContainers = match.findElements(By.xpath(".//obg-event-row-market-container"));
+
+            Optional<WebElement> matchOddsContainer = oddsContainers.stream()
+                    .filter(p -> p.findElement(By.xpath("//header/div/span"))
+                            .getText().equals("Matchresultat"))
+                    .findFirst();
+            float odds1 = -1f;
+            float oddsX = -1f;
+            float odds2 = -1f;
+            if (matchOddsContainer.isPresent()) {
+                List<WebElement> oddsValueContainers = matchOddsContainer.get()
+                        .findElements(By.xpath(".//section//span[@test-id='odds']"));
+
+                if (!oddsValueContainers.isEmpty()) {
+                    odds1 = formatFloat(oddsValueContainers.get(0).getText());
+                    oddsX = formatFloat(oddsValueContainers.get(1).getText());
+                    odds2 = formatFloat(oddsValueContainers.get(2).getText());
+                }
+
+            }
+            parsedMatches.add(new ResultDTO(homeTeamName, awayTeamName, odds1, oddsX, odds2));
+            System.out.println(homeTeamName + "-" + awayTeamName + " odds (" + odds1 + "," + oddsX + "," + odds2 + ")");
+        } catch (StaleElementReferenceException e) {
+            System.out.println("Stale element, refreshing");
+            driver.navigate().refresh();
+            parseMatch(driver, match);
+        }
+    }
+
+    private float formatFloat(String string) {
+        float result = -1f;
+        try {
+            result = Float.parseFloat(string);
+        } catch (NumberFormatException e) {
+            // Empty by design
+        }
+
+        return result;
+    }
+}

+ 1 - 0
Odds/src/parser/OddsPortal.java

@@ -72,6 +72,7 @@ public class OddsPortal implements ParserJoinedFunctions {
             webClient.getOptions().setCssEnabled(false);
             webClient.getOptions().setJavaScriptEnabled(true);
             webClient.getOptions().setThrowExceptionOnScriptError(false);
+            webClient.getOptions().setThrowExceptionOnFailingStatusCode(false);
             Logger.getLogger("com.gargoylesoftware").setLevel(Level.OFF);
 
             webClient.waitForBackgroundJavaScript(3000);

+ 53 - 0
Odds/src/parser/ParserBase.java

@@ -0,0 +1,53 @@
+package parser;
+
+import java.util.Collections;
+
+import org.openqa.selenium.By;
+import org.openqa.selenium.JavascriptExecutor;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.chrome.ChromeDriver;
+import org.openqa.selenium.chrome.ChromeOptions;
+
+public class ParserBase {
+
+    protected ChromeDriver getSeleniumDriver() {
+        ChromeOptions options = new ChromeOptions();
+
+        System.setProperty("webdriver.chrome.driver",
+                System.getProperty("user.dir") + "/src/main/resources/chromedriver.exe");
+        System.setProperty("webdriver.chrome.silentOutput", "true");
+        // Fixing 255 Error crashes
+        options.addArguments("--no-sandbox");
+        options.addArguments("--disable-dev-shm-usage");
+
+        // Options to trick bot detection
+        // Removing webdriver property
+        options.addArguments("--disable-blink-features=AutomationControlled");
+        options.setExperimentalOption("excludeSwitches", Collections.singletonList("enable-automation"));
+        options.setExperimentalOption("useAutomationExtension", null);
+
+        // Changing the user agent / browser fingerprint
+        options.addArguments("window-size=1920,1080");
+        options.addArguments(
+                "user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36");
+
+        // Other
+        options.addArguments("disable-infobars");
+
+        ChromeDriver driver = new ChromeDriver(options);
+        driver.executeScript("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})");
+        return driver;
+    }
+
+    protected boolean checkIfElementExists(WebElement element, String xpath) {
+        return !element.findElements(By.xpath(xpath)).isEmpty();
+    }
+
+    protected void scrollElementIntoView(ChromeDriver driver, WebElement element) {
+        ((JavascriptExecutor) driver).executeScript("arguments[0].scrollIntoView();", element);
+    }
+
+    protected void scrollElementIntoViewCenter(ChromeDriver driver, WebElement element) {
+        ((JavascriptExecutor) driver).executeScript("arguments[0].scrollIntoView({ block: 'center' });", element);
+    }
+}

+ 30 - 11
OddsJavaFx/src/controllers/AnalysisTestController.java

@@ -190,6 +190,8 @@ public class AnalysisTestController implements Initializable {
         activeBetsTable.getColumns().addAll(matchupColumn, oddsColumn, betAmountColumn, statusColumn, homeScoreColumn,
                 awayScoreColumn);
 
+        getActiveBets();
+
         statsLeagueNameColumn.setCellValueFactory(new MapValueFactory<>("statsLeagueName"));
         statsHomeWinColumn.setCellValueFactory(new MapValueFactory<>("statsHomeWin"));
         statsHomeLossColumn.setCellValueFactory(new MapValueFactory<>("statsHomeLoss"));
@@ -202,6 +204,13 @@ public class AnalysisTestController implements Initializable {
 
     }
 
+    private void getActiveBets() {
+        List<Bet> analysisBets = GuiMysql.getInstance().getAnalysisBets();
+
+        activeBetsTable.getItems().addAll(analysisBets);
+        activeBetsTable.refresh();
+    }
+
     private Float getNewBetValue(SoccerMatch newValue) {
         float odds = 0f;
         float currentDebt = 0f;
@@ -216,10 +225,11 @@ public class AnalysisTestController implements Initializable {
                     .get();
 
             Bet tempBet = bet;
-            while (tempBet.getMatch().getPreviousBet() != null) {
-                currentDebt += tempBet.getMatch().getPreviousBet().getBetAmount();
-                tempBet = tempBet.getMatch().getPreviousBet();
+            while (tempBet.getCoveredBetId() > 0) {
+                tempBet = GuiMysql.getInstance().getAnalysisBet(tempBet.getCoveredBetId());
+                currentDebt += tempBet.getBetAmount();
             }
+
             currentDebt += bet.getBetAmount();
             newValue.setPreviousBet(bet);
             if (newValue.getAnalysisValueInt() > 0f) {
@@ -266,11 +276,16 @@ public class AnalysisTestController implements Initializable {
     }
 
     private void setNewBet(SoccerMatch data, boolean isHomeBet) {
-        Bet bet = new Bet(-1, data, data.getBetAmount(), isHomeBet ? data.getOdds1() : data.getOdds2());
-        bet.setBetOnAwayTeam(!isHomeBet);
-        bet.setBetOnHomeTeam(isHomeBet);
+        int betId = GuiMysql.getInstance()
+                .addAnalysisBet(new Bet(-1, data, isHomeBet ? "1" : "2", data.getBetAmount(),
+                        isHomeBet ? data.getOdds1() : data.getOdds2()));
+
+        Bet bet = GuiMysql.getInstance().getAnalysisBet(betId);
+
         if (data.getPreviousBet() != null) {
             data.getPreviousBet().setStatus(Status.COVERED);
+            bet.setCoveredBetId(data.getPreviousBet().getId());
+            GuiMysql.getInstance().setBetCovered(bet.getId(), bet.getCoveredBetId());
             try {
                 activeBetsTable.getItems().set(activeBetsTable.getItems().indexOf(data.getPreviousBet()),
                         data.getPreviousBet());
@@ -283,8 +298,6 @@ public class AnalysisTestController implements Initializable {
         }
         activeBetsTable.getItems().add(bet);
         updateBank(-bet.getBetAmount());
-
-        GuiMysql.getInstance().addAnalysisBet(bet);
     }
 
     @FXML
@@ -365,15 +378,17 @@ public class AnalysisTestController implements Initializable {
     }
 
     private void checkBet(Bet b) {
-        if ((b.isBetOnHomeTeam() && b.getHomeScore() > b.getAwayScore())
-                || (b.isBetOnAwayTeam() && b.getHomeScore() < b.getAwayScore())) { // Win
+        if ((b.getBet().equals("1") && b.getHomeScore() > b.getAwayScore())
+                || (b.getBet().equals("2") && b.getHomeScore() < b.getAwayScore())) { // Win
             b.setStatus(Bet.Status.DONE);
             Float win = (b.getBetOdds() * b.getBetAmount());
             updateBank(win);
+
         } else { // Loss
             b.setStatus(Bet.Status.LOST);
         }
 
+        GuiMysql.getInstance().updateBetStatus(b.getId(), b.getStatus());
         updateStatsPanel(b);
     }
 
@@ -381,7 +396,8 @@ public class AnalysisTestController implements Initializable {
         String leagueName = b.getMatch().getLeagueName();
 
         Optional<Map<String, Object>> leagueStatsRow = LeagueBetStatsTable.getItems().stream()
-                .filter(p -> p.get("statsLeagueName").equals(leagueName)).findFirst();
+                .filter(p -> p.get("statsLeagueName") != null && leagueName.equals(p.get("statsLeagueName")))
+                .findFirst();
 
         if (leagueStatsRow.isEmpty()) {
             Map<String, Object> newItem = new HashMap<>();
@@ -480,6 +496,9 @@ public class AnalysisTestController implements Initializable {
                 .toList();
 
         for (Bet bet : doneBets) {
+            if (bet.getStatus().equals(Status.COVERED)) {
+                GuiMysql.getInstance().updateBetStatus(bet.getId(), Status.DONE);
+            }
             activeBetsTable.getItems().remove(bet);
         }
 

+ 75 - 13
OddsJavaFx/src/data/GuiMysql.java

@@ -6,6 +6,7 @@ import java.sql.Connection;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
+import java.sql.Statement;
 import java.text.DecimalFormat;
 import java.text.SimpleDateFormat;
 import java.time.LocalDateTime;
@@ -1148,7 +1149,7 @@ public class GuiMysql extends Mysql {
         String sql = "SELECT t.*, c.name as countryName, l.name AS leagueName FROM Team t "
                 + "INNER JOIN Country c ON t.countryId = c.id "
                 + "INNER JOIN League l ON t.leagueId = l.id "
-                + "WHERE id = ? ";
+                + "WHERE t.id = ? ";
         try (PreparedStatement stat = conn.prepareStatement(sql)) {
             stat.setInt(1, teamId);
 
@@ -1167,21 +1168,26 @@ public class GuiMysql extends Mysql {
     public List<Bet> getAnalysisBets() {
         List<Bet> result = new ArrayList<>();
         String sql = "SELECT * FROM AnalysisBetTable abt "
-                + "INNER JOIN SoccerResults sr ON abt.matchId = sr.id ";
+                + "INNER JOIN SoccerResults sr ON abt.matchId = sr.id "
+                + "WHERE status IN ('LOST', 'OPEN', 'COVERED')";
 
         try (PreparedStatement stat = conn.prepareStatement(sql)) {
             ResultSet rs = stat.executeQuery();
 
             while (rs.next()) {
 
-                SoccerMatch match = new SoccerMatch(rs.getInt("matchId"), getTeam(rs.getInt("homeTeamId")),
-                        getTeam(rs.getInt("awayTeamId")),
-                        rs.getFloat("odds1"),
+                Team homeTeam = getTeam(rs.getInt("homeTeamId"));
+                Team awayTeam = getTeam(rs.getInt("awayTeamId"));
+                SoccerMatch match = new SoccerMatch(rs.getInt("matchId"), homeTeam, awayTeam, rs.getFloat("odds1"),
                         rs.getFloat("oddsX"), rs.getFloat("odds2"), rs.getInt("homeScore"), rs.getInt("awayScore"),
                         LocalDateTime.parse(rs.getString("gameDate")), rs.getString("season"));
 
-                new Bet(rs.getInt("id"), match, rs.getFloat("betAmount"), rs.getFloat("betOdds"),
-                        Status.valueOf(rs.getString("status")));
+                match.setLeagueName(homeTeam.getTeamLeague());
+                match.setCountryName(homeTeam.getCountryName());
+
+                result.add(new Bet(rs.getInt("id"), match, rs.getString("bet"), rs.getFloat("betAmount"),
+                        rs.getFloat("betOdds"),
+                        Status.valueOf(rs.getString("status")), rs.getInt("coveredBetId")));
             }
 
         } catch (SQLException e) {
@@ -1190,16 +1196,72 @@ public class GuiMysql extends Mysql {
         return result;
     }
 
-    public void addAnalysisBet(Bet bet) {
-        String sql = "INSERT INTO AnalysisBetTable (matchId, betAmount, betOdds, status) VALUES (?,?,?,?)";
+    public int addAnalysisBet(Bet bet) {
+        int newId = -1;
+        String sql = "INSERT INTO AnalysisBetTable (matchId, bet, betAmount, betOdds, status) VALUES (?,?,?,?,?)";
 
-        try (PreparedStatement stat = conn.prepareStatement(sql)) {
+        try (PreparedStatement stat = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
             stat.setInt(1, bet.getMatch().getMatchId());
-            stat.setFloat(2, bet.getBetAmount());
-            stat.setFloat(3, bet.getBetOdds());
-            stat.setString(4, bet.getStatus().name());
+            stat.setString(2, bet.getBet());
+            stat.setFloat(3, bet.getBetAmount());
+            stat.setFloat(4, bet.getBetOdds());
+            stat.setString(5, bet.getStatus().name());
 
             stat.execute();
+
+            ResultSet generatedKeys = stat.getGeneratedKeys();
+
+            while (generatedKeys.next()) {
+                newId = generatedKeys.getInt(1);
+            }
+
+        } catch (SQLException e) {
+            e.printStackTrace();
+        }
+
+        return newId;
+    }
+
+    public Bet getAnalysisBet(int betId) {
+        Bet result = null;
+        String sql = "SELECT * FROM AnalysisBetTable abt "
+                + "INNER JOIN SoccerResults sr ON abt.matchId = sr.id "
+                + "WHERE abt.id = ?";
+
+        try (PreparedStatement stat = conn.prepareStatement(sql)) {
+            stat.setInt(1, betId);
+
+            ResultSet rs = stat.executeQuery();
+
+            while (rs.next()) {
+                Team homeTeam = getTeam(rs.getInt("homeTeamId"));
+                Team awayTeam = getTeam(rs.getInt("awayTeamId"));
+                SoccerMatch match = new SoccerMatch(rs.getInt("matchId"), homeTeam, awayTeam, rs.getFloat("odds1"),
+                        rs.getFloat("oddsX"), rs.getFloat("odds2"), rs.getInt("homeScore"), rs.getInt("awayScore"),
+                        LocalDateTime.parse(rs.getString("gameDate")), rs.getString("season"));
+
+                match.setLeagueName(homeTeam.getTeamLeague());
+                match.setCountryName(homeTeam.getCountryName());
+
+                result = new Bet(rs.getInt("id"), match, rs.getString("bet"), rs.getFloat("betAmount"),
+                        rs.getFloat("betOdds"),
+                        Status.valueOf(rs.getString("status")), rs.getInt("coveredBetId"));
+            }
+
+        } catch (SQLException e) {
+            e.printStackTrace();
+        }
+
+        return result;
+    }
+
+    public void setBetCovered(int id, int coveredBetId) {
+        String sql = "UPDATE AnalysisBetTable SET coveredBetId = ? WHERE id = ?";
+        try (PreparedStatement stat = conn.prepareStatement(sql)) {
+            stat.setInt(1, coveredBetId);
+            stat.setInt(2, id);
+
+            stat.executeUpdate();
         } catch (SQLException e) {
             e.printStackTrace();
         }

+ 1 - 1
OddsJavaFx/src/fxml/AnalysisTesting.fxml

@@ -36,7 +36,7 @@
                   </AnchorPane>
                 <AnchorPane>
                      <children>
-                        <SplitPane dividerPositions="0.6666666666666666" prefHeight="281.0" prefWidth="398.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
+                        <SplitPane dividerPositions="0.5" prefHeight="281.0" prefWidth="398.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
                           <items>
                             <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0">
                                  <children>

+ 2 - 2
OddsJavaFx/src/objects/bets/AsianHandicap.java

@@ -6,8 +6,8 @@ public class AsianHandicap extends Bet {
 
     float handicap;
 
-    public AsianHandicap(SoccerMatch match, float betAmount, float betOdds, float handicap) {
-        super(0, match, betAmount, betOdds);
+    public AsianHandicap(SoccerMatch match, String bet, float betAmount, float betOdds, float handicap) {
+        super(0, match, bet, betAmount, betOdds);
         this.handicap = handicap;
     }
 

+ 38 - 21
OddsJavaFx/src/objects/bets/Bet.java

@@ -7,9 +7,6 @@ public class Bet {
     SoccerMatch match;
     float betAmount;
     float betOdds;
-    boolean betOnHomeTeam;
-    boolean betOnDraw;
-    boolean betOnAwayTeam;
 
     int id;
     String homeTeamName;
@@ -17,6 +14,8 @@ public class Bet {
     String matchup;
     int homeScore;
     int awayScore;
+    int coveredBetId;
+    String bet;
 
     Status status;
 
@@ -28,14 +27,15 @@ public class Bet {
         SPLIT
     }
 
-    public Bet(int id, SoccerMatch match, float betAmount, float betOdds) {
-        new Bet(id, match, betAmount, betOdds, Status.OPEN);
+    public Bet(int id, SoccerMatch match, String bet, float betAmount, float betOdds) {
+        this(id, match, bet, betAmount, betOdds, Status.OPEN, -1);
     }
 
-    public Bet(int id, SoccerMatch match, float betAmount, float betOdds, Status status) {
+    public Bet(int id, SoccerMatch match, String bet, float betAmount, float betOdds, Status status, int coveredBetId) {
         this.betAmount = betAmount;
         this.betOdds = betOdds;
         this.match = match;
+        this.bet = bet;
 
         homeTeamName = match.getHomeTeam().getTeamName();
         awayTeamName = match.getAwayTeam().getTeamName();
@@ -45,6 +45,7 @@ public class Bet {
         awayScore = match.getAwayScore();
         this.status = status;
         this.id = id;
+        this.coveredBetId = coveredBetId;
     }
 
     public int getId() {
@@ -68,15 +69,15 @@ public class Bet {
     }
 
     public boolean isBetOnHomeTeam() {
-        return betOnHomeTeam;
+        return bet.equals("1");
     }
 
     public boolean isBetOnDraw() {
-        return betOnDraw;
+        return bet.equals("X");
     }
 
     public boolean isBetOnAwayTeam() {
-        return betOnAwayTeam;
+        return bet.equals("2");
     }
 
     public void setMatch(SoccerMatch match) {
@@ -91,23 +92,23 @@ public class Bet {
         this.betOdds = betOdds;
     }
 
-    public void setBetOnHomeTeam(boolean betOnHomeTeam) {
-        this.betOnHomeTeam = betOnHomeTeam;
+    public void setBetOnHomeTeam() {
+        this.bet = "1";
     }
 
-    public void setBetOnDraw(boolean betOnDraw) {
-        this.betOnDraw = betOnDraw;
+    public void setBetOnDraw() {
+        this.bet = "X";
     }
 
-    public void setBetOnAwayTeam(boolean betOnAwayTeam) {
-        this.betOnAwayTeam = betOnAwayTeam;
+    public void setBetOnAwayTeam() {
+        this.bet = "2";
     }
 
     public float getResult() {
         final float result;
-        if ((betOnHomeTeam && match.getHomeScore() > match.getAwayScore())
-                || (betOnDraw && match.getHomeScore() == match.getAwayScore())
-                || (betOnAwayTeam && match.getHomeScore() < match.getAwayScore())) {
+        if ((isBetOnHomeTeam() && match.getHomeScore() > match.getAwayScore())
+                || (isBetOnDraw() && match.getHomeScore() == match.getAwayScore())
+                || (isBetOnAwayTeam() && match.getHomeScore() < match.getAwayScore())) {
             result = betOdds * betAmount;
         } else {
             result = 0;
@@ -118,13 +119,13 @@ public class Bet {
 
     public boolean correctBet() {
         boolean result = false;
-        if (betOnHomeTeam && match.getHomeScore() > match.getAwayScore()) {
+        if (isBetOnHomeTeam() && match.getHomeScore() > match.getAwayScore()) {
             result = true;
         }
-        if (betOnDraw && match.getHomeScore() == match.getAwayScore()) {
+        if (isBetOnDraw() && match.getHomeScore() == match.getAwayScore()) {
             result = true;
         }
-        if (betOnAwayTeam && match.getHomeScore() < match.getAwayScore()) {
+        if (isBetOnAwayTeam() && match.getHomeScore() < match.getAwayScore()) {
             result = true;
         }
         return result;
@@ -182,4 +183,20 @@ public class Bet {
         this.status = status;
     }
 
+    public int getCoveredBetId() {
+        return coveredBetId;
+    }
+
+    public void setCoveredBetId(int coveredBetId) {
+        this.coveredBetId = coveredBetId;
+    }
+
+    public String getBet() {
+        return bet;
+    }
+
+    public void setBet(String bet) {
+        this.bet = bet;
+    }
+
 }

+ 3 - 6
OddsJavaFx/src/tests/AnalysisBettTester.java

@@ -163,9 +163,7 @@ public class AnalysisBettTester extends TestClass {
         boolean resolved = false;
 
         public DifferenceBetDTO(SoccerMatch match, float betAmount, boolean betOnHomeTeam) {
-            super(0, match, betAmount, betOnHomeTeam ? match.getOdds1() : match.getOdds2());
-            setBetOnHomeTeam(betOnHomeTeam);
-            setBetOnAwayTeam(!betOnHomeTeam);
+            super(0, match, betOnHomeTeam ? "1" : "2", betAmount, betOnHomeTeam ? match.getOdds1() : match.getOdds2());
         }
 
         @Override
@@ -183,9 +181,8 @@ public class AnalysisBettTester extends TestClass {
         boolean resolved = false;
 
         public DifferenceAsianBetDTO(SoccerMatch match, float betAmount, boolean betOnHomeTeam, float handicap) {
-            super(match, betAmount, betOnHomeTeam ? match.getOdds1() : match.getOdds2(), handicap);
-            setBetOnHomeTeam(betOnHomeTeam);
-            setBetOnAwayTeam(!betOnHomeTeam);
+            super(match, betOnHomeTeam ? "1" : "2", betAmount, betOnHomeTeam ? match.getOdds1() : match.getOdds2(),
+                    handicap);
         }
 
         @Override

+ 7 - 10
OddsJavaFx/src/tests/BetOnDifference.java

@@ -108,12 +108,12 @@ public class BetOnDifference extends TestClass {
                 if (currentBetAmount > 0) {
                     currentBetAmount = (float) (currentBetAmount / (soccerMatch.getOdds1() - 1.0));
                 }
-                bets.add(new DifferenceAsianBetDTO(soccerMatch, currentBetAmount + betAmount, true, 0f));
+                bets.add(new DifferenceAsianBetDTO(soccerMatch, "1", currentBetAmount + betAmount, true, 0f));
             } else {
                 if (currentBetAmount > 0) {
                     currentBetAmount = (float) (currentBetAmount / (soccerMatch.getOdds2() - 1.0));
                 }
-                bets.add(new DifferenceAsianBetDTO(soccerMatch, currentBetAmount + betAmount, false, 0f));
+                bets.add(new DifferenceAsianBetDTO(soccerMatch, "2", currentBetAmount + betAmount, false, 0f));
             }
 
             bank -= currentBetAmount + betAmount;
@@ -147,10 +147,8 @@ public class BetOnDifference extends TestClass {
     public class DifferenceBetDTO extends Bet {
         boolean resolved = false;
 
-        public DifferenceBetDTO(SoccerMatch match, float betAmount, boolean betOnHomeTeam) {
-            super(0, match, betAmount, betOnHomeTeam ? match.getOdds1() : match.getOdds2());
-            setBetOnHomeTeam(betOnHomeTeam);
-            setBetOnAwayTeam(!betOnHomeTeam);
+        public DifferenceBetDTO(SoccerMatch match, String bet, float betAmount, boolean betOnHomeTeam) {
+            super(0, match, bet, betAmount, betOnHomeTeam ? match.getOdds1() : match.getOdds2());
         }
 
         @Override
@@ -167,10 +165,9 @@ public class BetOnDifference extends TestClass {
     public class DifferenceAsianBetDTO extends AsianHandicap {
         boolean resolved = false;
 
-        public DifferenceAsianBetDTO(SoccerMatch match, float betAmount, boolean betOnHomeTeam, float handicap) {
-            super(match, betAmount, betOnHomeTeam ? match.getOdds1() : match.getOdds2(), handicap);
-            setBetOnHomeTeam(betOnHomeTeam);
-            setBetOnAwayTeam(!betOnHomeTeam);
+        public DifferenceAsianBetDTO(SoccerMatch match, String bet, float betAmount, boolean betOnHomeTeam,
+                float handicap) {
+            super(match, bet, betAmount, betOnHomeTeam ? match.getOdds1() : match.getOdds2(), handicap);
         }
 
         @Override