Football Scores, In-Play Stats & Momentum via script, using an API. Code and example BAF

Post Reply
sniffer66
Posts: 1666
Joined: Thu May 02, 2019 8:37 am

During the last lockdown I posted some simple code to pull football scores from the SofaScore API and pass to Guardian.

Thread here: viewtopic.php?f=6&t=22924&hilit=programatically

I only did it as a test and it had a few issues with name matching against the market name, given that team names (and even spelling) differ from BF and site to site i.e Manchester United, Man United, Manchester Utd, Man Utd. Horses, Tennis and Dogs etc have names that are common but football is a lot harder.

I've had a bit of spare time as I've been self isolating over Christmas, so to relieve the boredom I've updated the code. The name matching is improved by splitting out each word over 3 characters in the market name, ignoring commonly used words such as "City", "Town", "Hapoel", "Real" etc and then checking to see if at least 2 words are matched across both Home and Away team names. I would have liked to have used 3 or more words but some games only have 2 words in the Market name i.e "Rangers vs Celtic"

I've included some of the commonly used In-Play stats and I've also been able to grab the values in the Sofascore Momentum graph and calc 3 values (5 Min average, 10 Min average, Match Average)

Match Start time is also passed in each SV help prevent any mismatches.

Script:

This runs in a language called Auto-It. It's a fork of AutoHotKey and is pretty powerful.
To run the script you will need to download the program and Scite Editor from here:

https://www.autoitscript.com/site/autoit/downloads/

There are 2 additional User Defined Functions required called Json.au3 and BinaryCall.au3. I've put them in a shared folder on my Google Drive here:

https://drive.google.com/drive/folders/ ... sp=sharing

Copy both files to your Auto-It Includes folder (typically here C:\Program Files (x86)\AutoIt3\Include)

Once installed, paste the code below into the Scite Editor and save to a local folder as Football.au3 (or whatever name you wish)

Then, in Scite do "Tools - Go" and the script will start

The script will run in a 30s timed loop, pulling down the team names and current scores for all currently LIVE games in Sofascore. It will then save those results to a file called "c:\temp\sofascore.csv". You will need to create that folder if it doesnt exist already.



NOTES:
Be careful adjusting the speed of the script loop ( line 259). Lower than 10s and you risk your IP being blocked by SofaScore for too many accesses per minute.
Its possible to compile the script as an exe in Scite so you can just double click it whenever you have footy automation running. Scite - Tools - Compile. Just go with the defaults. Remember to kill (in the Task bar) when trading is over.

****************
Because of the way this is designed, there may be the odd file lockout where both Guardian and the script are trying to read/write the CSV file at the same time. BetAngel Support have confirmed that in this case Guardian will only read and use the entire file when unlocked. There is no chance of partial reads of the data and a problem with the SV's. In this case, the SV's will just update when the file is unlocked again i.e the script has finished writing. If this happens you will see a corresponding log entry in Guardian
****************

Script Edits.

Lines 17-20

;Turn Stats/Graph data on/off

$StatsOn = "Yes"
$GraphOn = "Yes"

Scores will always be returned but by editing "Yes" to "No" you can turn off either to improve the speed if you aren't using them, and only need the scores or just scores\stats. Momentum may prove slow at 3pm on a Sat as it has to loop through every value for the match - though I dont know the algorithm used by SS I assume its a combo of various stats so could be too useful to turn off.

NOTE: Many lesser league matches only provide the score - no stats or momentum. In those cases only the score is passed


Lines 87-88

;#########Uncomment the next line (remove the ";") to view available stats in the console on each loop
;Json_Dump($data3).

Uncommenting the Json_Dump command will dump all returned JSON code to the Scite console. This shows all available stats and allows you to add/edit your own in the script if you have the ability. Only use briefly as it really slows down the script.

Line 43
;Calls function to filter out unwanted match types (Women, U19 etc)
If _MatchCheck(Json_Get($object, '.events[' & $i & '].tournament.name')) = "Yes" Then

This line calls a function (at bottom of script) to check if the League (or a word in the league title) is not required and will ignore it if in the list. Editing out many of the leagues you don't trade will speed up the script on busy days and prevent getting data you dont need
Note: this may work better long term if it just picked the leagues required. May edit in the future


****************

The attached BAF will pull the created SV values into Guardian when applied to a match, and constantly write them to the log. You will want to turn off the log writes for each value once you are happy they are working, otherwise your log will fill quickly. I've also included an off the top of my head LTD rule just to demo the stats (no idea how that one would play out in a real match)
I've been testing in the MO markets but the code\baf will work on any markets that contain the team names in the title. I've also made all SV's Shared Event one's so can be used across all markets for the Event - CS, BTTS, MO etc. Run on MO and use in any related market you like :)

Log Example:

28/12/2021 08:21:33: [G_Auto 2] : Store Value (Shared) for event: HomeScore = 0
28/12/2021 08:21:33: [G_Auto 2] : Store Value (Shared) for event: AwayScore = 0
28/12/2021 08:21:33: [G_Auto 2] : Store Value (Shared) for event: SOTHome = 4
28/12/2021 08:21:33: [G_Auto 2] : Store Value (Shared) for event: SOTAway = 2
28/12/2021 08:21:33: [G_Auto 2] : Store Value (Shared) for event: SOFFTHome = 7
28/12/2021 08:21:33: [G_Auto 2] : Store Value (Shared) for event: SOFFTAway = 0
28/12/2021 08:21:33: [G_Auto 2] : Store Value (Shared) for event: CornersHome = 6
28/12/2021 08:21:33: [G_Auto 2] : Store Value (Shared) for event: CornersAway = 1
28/12/2021 08:21:33: [G_Auto 2] : Store Value (Shared) for event: BigChanceHome = 3
28/12/2021 08:21:33: [G_Auto 2] : Store Value (Shared) for event: BigChanceAway = 0
28/12/2021 08:21:33: [G_Auto 2] : Store Value (Shared) for event: PassesHome = 252
28/12/2021 08:21:33: [G_Auto 2] : Store Value (Shared) for event: PassesAway = 198
28/12/2021 08:21:33: [G_Auto 2] : Store Value (Shared) for event: PossessionHome = 57
28/12/2021 08:21:33: [G_Auto 2] : Store Value (Shared) for event: PossessionAway = 43
28/12/2021 08:21:33: [G_Auto 2] : Store Value (Shared) for event: 5MinAvgPressure = 11
28/12/2021 08:21:33: [G_Auto 2] : Store Value (Shared) for event: 10MinAvgPressure = 32
28/12/2021 08:21:33: [G_Auto 2] : Store Value (Shared) for event: MatchAvgPressure = 25


I've also added in a heartbeat "Update" SV that increments every script loop. By checking this SV has changed in the last say, 120s, your bot will know that stats are current and the script/API hasn't crashed and you are using old data

Hopefully I've covered everything off, but shout if you have any questions or suggestions. I was in IT but not a dev\coder by trade so this is all self taught stuff for me. There may be better\faster ways to do the above so feel free to chip in :)

Happy New Year !

Stu

Code: Select all

#Region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_Outfile=SS_Football.exe
#EndRegion ;**** Directives created by AutoIt3Wrapper_GUI ****

#include <Inet.au3>
#include <json.au3>
#include <Array.au3>
#include <String.au3>
#include <File.au3>
#include <Date.au3>



Local $HomeString, $AwayTeam1
$Update = 0

;Turn Stats/Graph data on/off

$StatsOn = "Yes"
$GraphOn = "Yes"



While 1

	$Update = $Update + 1

	Local $aMyTable, $aHoldingArray[0][100]

	$URL = "https://api.sofascore.com/api/v1/sport/football/events/live"
	$data = _INetGetSource($URL)
	;Json_Dump($data)

	$object = json_decode($data)

	Local $Count = Json_Get($object, '.events')
	;_ArrayDisplay($Count)


	For $i = 0 To UBound($Count) - 1

		;Calls function to filter out unwanted match types (Women, U19 etc)
		If _MatchCheck(Json_Get($object, '.events[' & $i & '].tournament.name')) = "Yes" Then

			;Set impossible starting values for Score
			$HomeScore = -1
			$AwayScore = -1

			$HomeTeam = Json_Get($object, '.events' & '[' & $i & '].homeTeam.name')
			$AwayTeam1 = Json_Get($object, '.events' & '[' & $i & '].awayTeam.name')
			$HomeScore = Json_Get($object, '.events' & '[' & $i & '].homeScore.current')
			$AwayScore = Json_Get($object, '.events' & '[' & $i & '].awayScore.current')

			$UnixTime = Json_Get($object, '.events' & '[' & $i & '].startTimestamp')

			$aCall = DllCall("msvcrt.dll", "str:cdecl", "ctime", "int*", $UnixTime)
			$tmp = StringSplit($aCall[0], " ")
			$StartTime = StringTrimRight($tmp[4],3)
		
			;MsgBox("", "", $HomeTeam & ":" & $AwayTeam1 & "   " & $HomeScore & ":" & $AwayScore)


			; Get API Stat data

			If $StatsOn = "Yes" Then

				;Set impossible starting values for Stats
				$PossessionHome = -1
				$PossessionAway = -1
				$SOTHome = -1
				$SOTAway = -1
				$SOFFTHome = -1
				$SOFFTAway = -1
				$CornersHome = -1
				$CornersAway = -1
				$BigChanceHome = -1
				$BigChanceAway = -1
				$PassesHome = -1
				$PassesAway = -1

				$URL = "https://api.sofascore.com/api/v1/event/" & Json_Get($object, '.events' & '[' & $i & '].id') & "/statistics"
				;MsgBox("", "", $URL)
				$data3 = _INetGetSource($URL)

				If $data3 <> "" Then

					;#########Uncomment the next line (remove the ";") to view available stats in the console on each loop
					;Json_Dump($data3)

					;MsgBox("", "", $HomeTeam & ":" & $AwayTeam1 & "   " & $HomeScore & ":" & $AwayScore)

					$object3 = json_decode($data3)

					Local $GroupCount = Json_Get($object3, '.statistics[0].groups')
					;_ArrayDisplay($GroupCount)

					For $c = 0 To UBound($GroupCount)

						If Json_Get($object3, '.statistics[0].groups[' & $c & '].statisticsItems[0].name') = "Ball possession" Then
							$PossessionHome = StringReplace(Json_Get($object3, '.statistics[0].groups[' & $c & '].statisticsItems[0].home '), "%", "")
							$PossessionAway = StringReplace(Json_Get($object3, '.statistics[0].groups[' & $c & '].statisticsItems[0].away'), "%", "")
							;MsgBox("", "", $PossessionHome & ":" & $PossessionAway)
						EndIf

						If Json_Get($object3, '.statistics[0].groups[' & $c & '].statisticsItems[1].name') = "Shots on target" Then
							$SOTHome = Json_Get($object3, '.statistics[0].groups[' & $c & '].statisticsItems[1].home')
							$SOTAway = Json_Get($object3, '.statistics[0].groups[' & $c & '].statisticsItems[1].away')
							;MsgBox("On", "On", $SOTHome & ":" & $SOTAway)
						EndIf

						If Json_Get($object3, '.statistics[0].groups[' & $c & '].statisticsItems[2].name') = "Shots off target" Then
							$SOFFTHome = Json_Get($object3, '.statistics[0].groups[' & $c & '].statisticsItems[2].home')
							$SOFFTAway = Json_Get($object3, '.statistics[0].groups[' & $c & '].statisticsItems[2].away')
							;MsgBox("Off", "Off", $SOFFTHome & ":" & $SOFFTAway)
						EndIf

						If Json_Get($object3, '.statistics[0].groups[' & $c & '].statisticsItems[0].name') = "Corner kicks" Then
							$CornersHome = Json_Get($object3, '.statistics[0].groups[' & $c & '].statisticsItems[0].home')
							$CornersAway = Json_Get($object3, '.statistics[0].groups[' & $c & '].statisticsItems[0].away')
							;MsgBox("Corners", "Corners", $CornersHome & ":" & $CornersAway)
						EndIf

						If Json_Get($object3, '.statistics[0].groups[' & $c & '].statisticsItems[0].name') = "Big chances" Then
							$BigChanceHome = Json_Get($object3, '.statistics[0].groups[' & $c & '].statisticsItems[0].home')
							$BigChanceAway = Json_Get($object3, '.statistics[0].groups[' & $c & '].statisticsItems[0].away')
							;MsgBox("Chance", "Chance", $BigChanceHome & ":" & $BigChanceAway)
						EndIf

						If Json_Get($object3, '.statistics[0].groups[' & $c & '].statisticsItems[0].name') = "Passes" Then
							$PassesHome = Json_Get($object3, '.statistics[0].groups[' & $c & '].statisticsItems[0].home')
							$PassesAway = Json_Get($object3, '.statistics[0].groups[' & $c & '].statisticsItems[0].away')
							;MsgBox("Passes", "Passes", $PassesHome & ":" & $PassesAway)
						EndIf

					Next
				EndIf
			EndIf

			; Get API Graph data
			If $GraphOn = "Yes" Then

				;Set impossible starting values for Graph
				$5MinAvgPressure = -100
				$10MinAvgPressure = -100
				$MatchAvgPressure = -100

				$URL = "https://api.sofascore.com/api/v1/event/" & Json_Get($object, '.events' & '[' & $i & '].id') & "/graph "
				;MsgBox("", "", $URL)
				$data2 = _INetGetSource($URL)
				If $data2 <> "" Then



					;Json_Dump($data2)


					$object2 = json_decode($data2)
					Local $Count = Json_Get($object2, '.graphPoints')

					;_ArrayDisplay($Count)

					;5 Min Average
					$GraphTotal = 0
					$GraphCount = 0
					For $y = UBound($Count) - 1 To 0 Step -1
						;$Convert = StringReplace(Json_Get($object2, '.graphPoints' & '[' & $y & '].value'), "-", "")
						$Convert = Json_Get($object2, '.graphPoints' & '[' & $y & '].value')
						$GraphTotal = $GraphTotal + $Convert
						$GraphCount = $GraphCount + 1
						If $GraphCount = 5 Then ExitLoop
					Next

					$GraphAverage = 0
					$GraphAverage = Round($GraphTotal / $GraphCount)
					If StringInStr($GraphAverage, "IND") Then
						$5MinAvgPressure = 99
					Else
						$5MinAvgPressure = $GraphAverage
					EndIf

					;10 Min Average
					$GraphTotal = 0
					$GraphCount = 0
					For $y = UBound($Count) - 1 To 0 Step -1
						;$Convert = StringReplace(Json_Get($object2, '.graphPoints' & '[' & $y & '].value'), "-", "")
						$Convert = Json_Get($object2, '.graphPoints' & '[' & $y & '].value')
						$GraphTotal = $GraphTotal + $Convert
						$GraphCount = $GraphCount + 1
						If $GraphCount = 10 Then ExitLoop
					Next

					$GraphAverage = 0
					$GraphAverage = Round($GraphTotal / $GraphCount)
					If StringInStr($GraphAverage, "IND") Then
						$10MinAvgPressure = 99
					Else
						$10MinAvgPressure = $GraphAverage
					EndIf

					;Match Average
					$GraphTotal = 0
					$GraphCount = 0
					For $y = UBound($Count) - 1 To 0 Step -1
						;$Convert = StringReplace(Json_Get($object2, '.graphPoints' & '[' & $y & '].value'), "-", "")
						$Convert = Json_Get($object2, '.graphPoints' & '[' & $y & '].value')
						$GraphTotal = $GraphTotal + $Convert
						$GraphCount = $GraphCount + 1
					Next

					$GraphAverage = 0
					$GraphAverage = Round($GraphTotal / $GraphCount)
					If StringInStr($GraphAverage, "IND") Then
						$MatchAvgPressure = 99
					Else
						$MatchAvgPressure = $GraphAverage
					EndIf

				EndIf
			EndIf

			Local $TeamFinal = _ReturnTeams()

			For $a = 0 To UBound($TeamFinal) - 1

				$BuildString = "*" & _StringReplaceAccent($TeamFinal[$a]) & "*|" & $StartTime & "|*|*|E|Name" & $a & "|1|E|HomeScore1|" & $HomeScore & "|E|AwayScore1|" & $AwayScore

				If $StatsOn = "Yes" Then
					$BuildString = $BuildString & "|E|SOTHome1|" & $SOTHome & "|E|SOTAway1|" & $SOTAway & "|E|SOFFTHome1|" & $SOFFTHome & "|E|SOFFTAway1|" & $SOFFTAway & "|E|CornersHome1|" & $CornersHome & "|E|CornersAway1|" & $CornersAway & "|E|BigChanceHome1|" & $BigChanceHome & "|E|BigChanceAway1|" & $BigChanceAway & "|E|PassesHome1|" & $PassesHome & "|E|PassesAway1|" & $PassesAway & "|E|PossessionHome1|" & $PossessionHome & "|E|PossessionAway1|" & $PossessionAway
				EndIf

				If $GraphOn = "Yes" Then
					$BuildString = $BuildString & "|E|5MinAvgPressure1|" & $5MinAvgPressure & "|E|10MinAvgPressure1|" & $10MinAvgPressure & "|E|MatchAvgPressure1|" & $MatchAvgPressure
				EndIf

				_ArrayAdd($aHoldingArray, $BuildString)

			Next


		EndIf

	Next

	;Add Heartbeat to array/update
	_ArrayAdd($aHoldingArray, "*|*|*|*|E|Update|" & $Update)

	;_ArrayDisplay($aHoldingArray)


	_FileWriteFromArray("c:\temp\sofascore.csv", $aHoldingArray, Default, Default, ",")
	_FileWriteToLine("c:\temp\sofascore.csv", 1, "1", False)

	ConsoleWrite(_NowTime() & @CRLF)


	;Wait time between score refresh in milliseconds
	;#######DO NOT SET SHORTER THAN 10S AS YOUR IP MAY BE BLOCKED#####

	Sleep(30000) ;30 seconds


WEnd



Func _NameCheck($Word)
	If StringInStr($Word, "http") Then
		Return "No"
	ElseIf StringInStr($Word, "vs") Then
		Return "No"
	ElseIf StringInStr($Word, "Hapoel") Then
		Return "No"
	ElseIf StringInStr($Word, "Club") Then
		Return "No"
	ElseIf StringInStr($Word, "City") Then
		Return "No"
	ElseIf StringInStr($Word, "Town") Then
		Return "No"
	ElseIf StringInStr($Word, "U23") Then
		Return "No"
	ElseIf StringInStr($Word, "Utd") Then
		Return "No"
	ElseIf StringInStr($Word, "VPV") Then
		Return "No"
	ElseIf StringInStr($Word, "VPS") Then
		Return "No"
	ElseIf StringInStr($Word, "Citizen") Then
		Return "No"
	ElseIf StringInStr($Word, "Club") Then
		Return "No"
	ElseIf StringInStr($Word, "TSV") Then
		Return "No"
	ElseIf StringInStr($Word, "TSG") Then
		Return "No"
	ElseIf StringInStr($Word, "Tblisi") Then
		Return "No"
	ElseIf StringInStr($Word, "Women") Then
		Return "No"
	ElseIf StringInStr($Word, "U16") Then
		Return "No"
	ElseIf StringInStr($Word, "U17") Then
		Return "No"
	ElseIf StringInStr($Word, "U19") Then
		Return "No"
	ElseIf StringInStr($Word, "U21") Then
		Return "No"
	ElseIf StringInStr($Word, "U23") Then
		Return "No"
	ElseIf StringInStr($Word, "U20") Then
		Return "No"
	ElseIf StringInStr($Word, "(W)") Then
		Return "No"
	ElseIf StringInStr($Word, "United") Then
		Return "No"
	ElseIf StringInStr($Word, "Maccabi") Then
		Return "No"
	ElseIf StringInStr($Word, "Viit") Then
		Return "No"
	ElseIf StringInStr($Word, "USD") Then
		Return "No"
	ElseIf StringInStr($Word, "Beitar") Then
		Return "No"
	ElseIf StringInStr($Word, "Bucu") Then
		Return "No"
	ElseIf StringInStr($Word, "Buch") Then
		Return "No"
	ElseIf StringInStr($Word, "FCV") Then
		Return "No"
	ElseIf StringInStr($Word, "Spart") Then
		Return "No"
	ElseIf StringInStr($Word, "Unirea") Then
		Return "No"
	ElseIf StringInStr($Word, "Dinamo") Then
		Return "No"
	ElseIf StringInStr($Word, "Dynamo") Then
		Return "No"
	ElseIf StringInStr($Word, "Tblis") Then
		Return "No"
	ElseIf StringInStr($Word, "Youth") Then
		Return "No"
	ElseIf StringInStr($Word, "Atletico") Then
		Return "No"
	ElseIf StringInStr($Word, "Athletic") Then
		Return "No"
	ElseIf StringInStr($Word, "Real") Then
		Return "No"
	Else
		Return "Yes"
	EndIf
EndFunc   ;==>_NameCheck


Func _StringReplaceAccent($sString)
	Local $exp, $rep
	Local $Pattern[29][2] = [ _
			["[ÀÁÂÃÅÆ]", "A"], ["[àáâãåą]", "a"], ["Ä", "Ae"], ["[æä]", "ae"], _
			["Þ", "B"], ["þ", "b"], _
			["ÇĆ", "C"], ["[çćč]", "c"], _
			["[ÈÉÊË]", "E"], ["[èéêë]", "e"], _
			["[ÌÍÎÏ]", "I"], ["[ìíîï]", "i"], _
			["Ñ", "N"], ["ñ", "n"], _
			["[ÒÓÔÕÖØ]", "O"], ["[ðòóôõöø]", "o"], _
			["ř", "r"], _
			["[ŠŚ]", "S"], ["[š]", "s"], _
			["ß", "Ss"], _
			["Ț", "T"], _
			["[ÙÚÛ]", "U"], ["[ùúû]", "u"], ["Ü", "Ue"], ["ü", "ue"], _
			["Ý", "Y"], ["[ýýÿ]", "y"], _
			["Ž", "Z"], ["ž", "z"]]

	For $i = 0 To (UBound($Pattern) - 1)
		$exp = $Pattern[$i][0]
		If $exp = "" Then ContinueLoop
		$rep = $Pattern[$i][1]

		$sString = StringRegExpReplace($sString, $exp, $rep)
		If @error == 0 And @extended > 0 Then
			;ConsoleWrite($sString & @LF & "--> " & $exp & @LF)
		EndIf
	Next

	Return $sString
EndFunc   ;==>_StringReplaceAccent


Func _MatchCheck($Word)
	If StringInStr($Word, "Esports") Then
		Return "No"
	ElseIf StringInStr($Word, "Friend") Then
		Return "No"
		;ElseIf StringInStr($Word, "Women") Then
		Return "No"
		;ElseIf StringInStr($Word, "(W)") Then
		Return "No"
	ElseIf StringInStr($Word, "Reserves") Then
		Return "No"
	ElseIf StringInStr($Word, "U16") Then
		Return "No"
	ElseIf StringInStr($Word, "U17") Then
		Return "No"
	ElseIf StringInStr($Word, "Youth") Then
		Return "No"
		;ElseIf StringInStr($Word, "U19") Then
		;	Return "No"
		;ElseIf StringInStr($Word, "U21") Then
		;	Return "No"
		;ElseIf StringInStr($Word, "U20") Then
		;	Return "No"
		;ElseIf StringInStr($Word, "Cup") Then
		;	Return "No"
		;ElseIf StringInStr($Word, "Copa") Then
		;	Return "No"
	Else
		Return "Yes"
	EndIf
EndFunc   ;==>_MatchCheck




Func _ReturnTeams()

	Local $TeamArray = StringSplit($HomeTeam, " ", 2)
	Local $AwayTeam = StringSplit($AwayTeam1, " ", 2)
	Local $TeamFinalFunc[0]
	_ArrayConcatenate($TeamArray, $AwayTeam)

	For $f = 0 To UBound($TeamArray) - 1
		$TeamArray[$f] = StringStripWS($TeamArray[$f], 8)
	Next

	;_ArrayDisplay($TeamArray)


	For $x = 0 To UBound($TeamArray) - 1


		If StringInStr($TeamArray[$x], "-") And StringLen($TeamArray[$x]) > 4 Then
			$HyphenSplit = StringSplit($TeamArray[$x], "-", 2)
			;_ArrayDisplay($HyphenSplit)
			For $d = 0 To UBound($HyphenSplit) - 1
				If StringLen($HyphenSplit[$d]) > 3 And _NameCheck($TeamArray[$x]) = "Yes" Then
					_ArrayAdd($TeamFinalFunc, StringLeft($HyphenSplit[$d], 6))
				EndIf
			Next

		ElseIf StringLen($TeamArray[$x]) > 3 And _NameCheck($TeamArray[$x]) = "Yes" Then
			_ArrayAdd($TeamFinalFunc, StringLeft($TeamArray[$x], 6))
		EndIf
	Next

	;_ArrayDisplay($TeamFinalFunc)


	Return ($TeamFinalFunc)


EndFunc   ;==>_ReturnTeams






SofaScore V1.1.baf
You do not have the required permissions to view the files attached to this post.
Last edited by sniffer66 on Wed Dec 29, 2021 8:46 am, edited 1 time in total.
sniffer66
Posts: 1666
Joined: Thu May 02, 2019 8:37 am

Notes on the Momentum Graph:

The graph centres around a 0 axis. If both teams momentum is the same i.e 50/50, then the current momentum is 0. if Home are in the ascendancy the result is positive (between +1 and +99), if the away team then it's between -1 and --99. The bigger the value the greater the momentum

Sofascore obviously don't publish the data used to create the Momentum figure, but I assume its a combo of Possession, SOT's, SOFFT's, Corners, DA's etc so should be very useful in it's own right.
sniffer66
Posts: 1666
Joined: Thu May 02, 2019 8:37 am

Example on how the name matching and CSV work:

In this match, the teams have 5 separate, non common, words over 3 characters ( non standard foreign chars are replaced)

SS2.JPG


So, the script separates each name word into an SV line in the CSV (all with the same stat values, if they exist)

SS1.JPG

Each name entry is numbered (In this case 0 to 4). If two of the names match against the market name then the common stat values are used. If less than 2 they are ignored
You do not have the required permissions to view the files attached to this post.
User avatar
Euler
Posts: 24701
Joined: Wed Nov 10, 2010 1:39 pm
Location: Bet Angel HQ

Many thanks for your excellent work.
footysystems
Posts: 165
Joined: Thu May 09, 2013 5:57 pm

Excellent post Stuart that will be a real help to some people.
It's a real shame they only cover a certain amount of matches.
sniffer66
Posts: 1666
Joined: Thu May 02, 2019 8:37 am

Euler wrote:
Wed Dec 29, 2021 9:16 am
Many thanks for your excellent work.
No problem Peter, hopefully it's going to be useful
sniffer66
Posts: 1666
Joined: Thu May 02, 2019 8:37 am

footysystems wrote:
Wed Dec 29, 2021 9:22 am
Excellent post Stuart that will be a real help to some people.
It's a real shame they only cover a certain amount of matches.
Yeah, that's why I use a paid for API myself. However, you can't really moan when it's free 😁
sniffer66
Posts: 1666
Joined: Thu May 02, 2019 8:37 am

Another good way I can think to use some of the stats is to save the stats to History Lists in Guardian, then query those values in your entry rules.

I.e is SOT, big chance etc value 1 or more greater than 10m ago. So as well as efforts on goal over the match you know a team is pushing recently as well as using the momentum graph data.
sionascaig
Posts: 1053
Joined: Fri Nov 20, 2015 9:38 am

Fantastic job.... Well impressed...

I hit the "name" problem at the selection level for markets I'm interested in. If there was an easy solution would have been happy to share it but I don't think there is - it requires a constant review / modification

Names will revert back to an older version or a new one will appear & when you bring in the euro markets can be dealing with all the language specific differences as well. Got cases with 3 / 4 (bf) names for same player just in last year.

One thing that can be quite useful, depending on what you are doing though is the inclusion of custom columns on the one-click screen which will allow you to display / over right imported SV's on the fly...

Anyhow - great work )
sniffer66
Posts: 1666
Joined: Thu May 02, 2019 8:37 am

sionascaig wrote:
Wed Dec 29, 2021 10:44 am
Fantastic job.... Well impressed...

I hit the "name" problem at the selection level for markets I'm interested in. If there was an easy solution would have been happy to share it but I don't think there is - it requires a constant review / modification

Names will revert back to an older version or a new one will appear & when you bring in the euro markets can be dealing with all the language specific differences as well. Got cases with 3 / 4 (bf) names for same player just in last year.

One thing that can be quite useful, depending on what you are doing though is the inclusion of custom columns on the one-click screen which will allow you to display / over right imported SV's on the fly...

Anyhow - great work )

Cheers :D

Yup, the name issue is a nightmare for 100% accuracy. I've tried to mitigate that by wildcarding and only using the first 6 characters but know there will still be errors. Some kind of fuzzy "is like" matching may do it but....
footysystems
Posts: 165
Joined: Thu May 09, 2013 5:57 pm

sniffer66 wrote:
Wed Dec 29, 2021 10:08 am
footysystems wrote:
Wed Dec 29, 2021 9:22 am
Excellent post Stuart that will be a real help to some people.
It's a real shame they only cover a certain amount of matches.
Yeah, that's why I use a paid for API myself. However, you can't really moan when it's free 😁
Yep free is free ☺️
sionascaig
Posts: 1053
Joined: Fri Nov 20, 2015 9:38 am

Just thinking about the name issue - might be good to have a servant with the name change logic in it that can be run constantly... Not tried it yet but should be relatively easy to maintain on the hoof...
asaele
Posts: 22
Joined: Fri Jul 02, 2021 8:45 am

This is great stuff. Thanks a lot for posting.

Been using betangel for a long time, but just recently started discovering the enormous possibilities.
And all the fully working example posts make it even better!
sniffer66
Posts: 1666
Joined: Thu May 02, 2019 8:37 am

sionascaig wrote:
Wed Dec 29, 2021 10:44 am
Fantastic job.... Well impressed...

I hit the "name" problem at the selection level for markets I'm interested in. If there was an easy solution would have been happy to share it but I don't think there is - it requires a constant review / modification

Names will revert back to an older version or a new one will appear & when you bring in the euro markets can be dealing with all the language specific differences as well. Got cases with 3 / 4 (bf) names for same player just in last year.

One thing that can be quite useful, depending on what you are doing though is the inclusion of custom columns on the one-click screen which will allow you to display / over right imported SV's on the fly...

Anyhow - great work )
Sionas, I think I've gone a long way, if not 100% of the way to solving the name matching issue.

Not sure what your coding is like but hopefully you can follow, using the SofaScore code I've already posted as an example. I got the idea from another post earlier today

The issue is that it's very hard to do any pattern based matching in Guardian, but easier to do in code. So, I had the idea to extract the current market name using an "Export data from CSV" rule, every x seconds. That gives you the current market name alongside any set SV's
By reading the exported CSV into my script as an array, or array of arrays, I can then do some decent string comparison checking against the market name string and pass SV values for that exact market name back into Guardian using my existing "Import data from CSV" rule


SS1.JPG

Auto-It code then looks like this:

(Imports the exported CSV to an array of arrays, the creates a new 1D array, which is a list of markets the baf is currently applied to, and are inplay. Export file is deleted each loop other wise multiple resuts are appended each export. I then build an array of unique matches in play to string compare against)

Code: Select all

If FileExists("C:\Temp\SS_Multi.csv") Then
		_FileReadToArray("C:\Temp\SS_Multi.csv", $Markets, 2, ",")

		For $x = 0 To UBound($Markets) - 1
			_ArraySearch($MarketList, ($Markets[$x])[1])
			If @error Then
				_ArrayAdd($MarketList, ($Markets[$x])[1])
				;_ArrayDisplay($MarketList)
			EndIf
		Next

		FileDelete("C:\Temp\SS_Multi.csv")
	EndIf
I can then do some string comparison checks to see if certain strings appear or are very close to the strings in the BF market name (just testing these simply atm but can do a bit more advanced "string is like" checking in the future)

Code: Select all

$Matched = 0

For $u = 0 To UBound($MarketList) - 1
	If $Matched = 2 Then ExitLoop
		For $t = 0 To UBound($TeamFinal) - 1
			If StringInStr($MarketList[$u], $TeamFinal[$t]) Then
				$Matched = $Matched + 1
				If $Matched = 2 Then
					$MarketFinal = $MarketList[$u]
					ExitLoop
				EndIf
			EndIf
		Next
Next

If $Matched = 2 Then

Code: Select all

$BuildString = $MarketFinal & "|*|*|*|E|HomeScoreSS|" & $HomeScore & "|E|AwayScoreSS|" & $AwayScore & "|E|Momentum|" & $Momentum
The beauty of doing it this way is you are passing back the EXACT market\selection name that BF is using, plus you only need to query external stats\data for markets your BAF is applied to, all other scraped\queried data can be ignored as it's not needed

Hope that makes sense :)
You do not have the required permissions to view the files attached to this post.
Last edited by sniffer66 on Thu Jan 06, 2022 1:27 pm, edited 2 times in total.
User avatar
Dallas
Posts: 22674
Joined: Sun Aug 09, 2015 10:57 pm
Location: Working From Home

I must have have missed this topic appearing over the Xmas hols

but its looks an impressive piece of work sniffer66 and of course extra thanks for sharing

Image
Post Reply

Return to “Bet Angel Automation - Football”