class Application(object): settings = None log = system = None # Placeholders to be filled later. growl = None # Can be 'None' if no Growl found. def __init__(self, is_daemon=True): self.settings = self._assembleSettings(is_daemon=is_daemon) self.log = Logger(self) self.log.install() self.log.debug("Logging installed.") self.system = System(self) self.log.debug("Application initialisation finished.") def _assembleSettings(self, is_daemon): """Assemple application settings (such as log dir, etc).""" return _AppConfig({ "is_daemon": is_daemon, "log_dir": os.path.join(os.path.dirname(__file__), "logs"), }) def doWork(self): """Called when caller wants for application to perform its actual functionality.""" self.system.loadPlugins() self.log.debug("System plugins loaded.") # Growl app is so specific and widespread that it makes sense to # care about it even on the top application level. if self.system.plugins.growl.isRunning(): self.growl = self.system.plugins.growl self.growl.setGlobalGrowlingApplicationOptions("OsXRuleActions") # Now to call call user configuration. from config import main as mainConfig try: mainConfig.execute(self.system) except: self.log.exception("Error happened during config run.") @classmethod def consoleRun(cls, options): """Called when application is initialized from console.""" _app = cls(is_daemon=options.is_daemon) try: _app.doWork() except: _app.showBadException("Error in main application loop.") def showBadException(self, msg): """Show top-level exception. Called only when everything else fails.""" _str = StringIO() traceback.print_exc(file=_str) _str.seek(0, 0) _str = _str.read() _pop = subprocess.Popen("open -tf", shell=True, stdin=subprocess.PIPE) _pop.stdin.write("Error happend: %s\n%s" % (msg, _str)) _pop.communicate()
def footyBackTest(resultsURLTmpl, opts=sys.argv): (algoCfg, mailCfg) = getFootyConfig() rangeMap = algoCfg['rangeMap'] seasons = algoCfg['seasons'] log = Logger() (sm, rm) = getFootyOptions(log, opts) rangeMap = rm if rm else rangeMap for league in rangeMap.keys(): summaryData = {} with readCSVFileAsDict('{}/{}/Summary.{}.csv'.format( analysisDir, league, model.__class__.__name__)) as summaryReader: for row in summaryReader: mark = int(row['Mark']) summaryData[mark] = { 'H': (float(row['%H']), float(row['HO'])), 'D': (float(row['%D']), float(row['DO'])), 'A': (float(row['%A']), float(row['AO'])) } with newCSVFile( '{}/{}/BackTest.{}.csv'.format(analysisDir, league, model.__class__.__name__), [ 'Date', 'HomeTeam', 'AwayTeam', 'Mark', 'Result', 'MyBet', 'MyOdds', 'Bookie', 'BookieOdds', 'Winnings', 'PnL', 'T_Stk', 'T_W', 'Yield' ]) as backTestWriter: ts = tw = y = 0 for season in seasons: resultsURL = resultsURLTmpl.format(season, league) log.debug('Processing...{}'.format(resultsURL)) with readCSVFileAsDict(resultsURL) as resultsReader: # Assemble results as list so that we can reset the iterator res = list(resultsReader) data = model.processMatches(res) # Resetting the iterator here for row in iter(res): date, ht, at, mark, hForm, aForm = model.markMatch( data, row['Date'], row['HomeTeam'], row['AwayTeam']) if mark is None: continue if mark in rangeMap[league]: bestH = 0 bestD = 0 bestA = 0 bookie = '' try: b365H = float(row['B365H']) b365D = float(row['B365D']) b365A = float(row['B365A']) if b365H > bestH: bestH = b365H bookie = 'B365' except BaseException: log.error('No B365 data - skipping : {} {} {}'\ .format(date, ht, at)) try: bwH = float(row['BWH']) bwD = float(row['BWD']) bwA = float(row['BWA']) if bwH > bestH: bestH = bwH bookie = 'BW' except BaseException: log.error('No BW data - skipping : {} {} {}'\ .format(date, ht, at)) try: iwH = float(row['IWH']) iwD = float(row['IWD']) iwA = float(row['IWA']) if iwH > bestH: bestH = iwH bookie = 'IW' except BaseException: log.error('No IW data - skipping : {} {} {}'\ .format(date, ht, at)) try: lbH = float(row['LBH']) lbD = float(row['LBD']) lbA = float(row['LBA']) if lbH > bestH: bestH = lbH bookie = 'LB' except BaseException: log.error('No LB data - skipping : {} {} {}'\ .format(date, ht, at)) try: whH = float(row['WHH']) whD = float(row['WHD']) whA = float(row['WHA']) if whH > bestH: bestH = whH bookie = 'WH' except BaseException: log.error('No WH data - skipping : {} {} {}'\ .format(date, ht, at)) try: vcH = float(row['VCH']) vcD = float(row['VCD']) vcA = float(row['VCA']) if vcH > bestH: bestH = vcH bookie = 'VC' except BaseException: log.error('No VC data - skipping : {} {} {}'\ .format(date, ht, at)) hSD = summaryData[mark]['H'] aSD = summaryData[mark]['A'] dSD = summaryData[mark]['D'] myBet = '' myOdds = 0.0 myPercent = 0.0 bookieOdds = 0.0 winnings = 0.0 pnl = 0.0 if bestH > hSD[1]: # and bestH < (hSD[1] * 2): myBet = 'H' myOdds = hSD[1] #myOdds = (1.97*mark+45.42)*0.9 myPercent = hSD[0] bookieOdds = bestH winnings = bookieOdds pnl = winnings - 1 if False and myPercent < dSD[0] and bestD > dSD[1]: #if myPercent < dSD[0] and b365D > dSD[1]: myBet = 'D' myOdds = dSD[1] myPercent = dSD[0] bookieOdds = bestD winnings = bookieOdds pnl = winnings - 1 if False and myPercent < aSD[0] and bestA > aSD[1]: #if myPercent < aSD[0] and b365A > aSD[1]: myBet = 'A' myOdds = aSD[1] myPercent = aSD[0] bookieOdds = bestA winnings = bookieOdds pnl = winnings - 1 matchResult = row['FTR'] if myBet != '': if matchResult != myBet: winnings = 0.0 pnl = -1.0 ts += 1 tw += winnings y = (tw - ts) / ts backTestWriter.writerow( (date, ht, at, mark, matchResult, myBet, myOdds, bookie, bookieOdds, winnings, pnl, ts, tw, y)) log.info( '{:<5s} - Staked: GBP{:>6.2f} Won: GBP{:>6.2f} Yield: {:>6.2f}%'. format(league, ts, tw, y * 100))
def makeFootyHistory(resultsURLTmpl, opts=sys.argv): log = Logger() getFootyOptions(log, opts) (algoCfg, mailCfg) = getFootyConfig() rangeMap = algoCfg['rangeMap'] seasons = algoCfg['seasons'] ''' Looks like if you go back too far with the historical data it starts to mess up the results, I suspect this is because the league composition has changed enough to mean that the newer and older season data don't play well together... ''' log.info(__name__ + ' : ' + model.__class__.__name__) for league in rangeMap.keys(): log.info('League : {}...'.format(league)) os.makedirs('{}/{}'.format(analysisDir, league), exist_ok=True) summaryData = {} with newCSVFile('{}/{}/History.{}.csv'.format(analysisDir, league, model.__class__.__name__), ['Date', 'HomeTeam', 'AwayTeam', 'Mark', 'Result']) \ as historyWriter: for season in seasons: resultsURL = resultsURLTmpl.format(season, league) log.debug('Processing...{}'.format(resultsURL)) try: with readCSVFileAsDict(resultsURL) as resultsReader: # Assembling as list so that the iterator can be reset res = list(resultsReader) data = model.processMatches(res) # Resetting iterator here... for row in iter(res): try: date, ht, at, mark, hForm, aForm = \ model.markMatch(data, row['Date'], row['HomeTeam'], row['AwayTeam']) except KeyError: continue if mark is None or row['FTR'] == '': continue mark = int(mark) matchResult = row['FTR'].strip() historyWriter.writerow( [date, ht, at, mark, matchResult]) if mark not in summaryData: summaryData[mark] = {'A': 0, 'D': 0, 'H': 0} summaryData[mark][matchResult] += 1 except BaseException: log.error(sys.exc_info()[0:1]) continue log.info('Writing summary data...') with newCSVFile('{}/{}/Summary.{}.csv'.format(analysisDir, league, model.__class__.__name__), ['Mark', 'Frequency', '%H','HO', '%D', 'DO', '%A', 'AO']) \ as summaryWriter: x = [] hY = [] dY = [] aY = [] hist = {} for mark in summaryData: if mark > 15 or mark < -15: continue awayF = summaryData[mark]['A'] drawF = summaryData[mark]['D'] homeF = summaryData[mark]['H'] totalF = awayF + drawF + homeF awayP = awayF / totalF * 100 drawP = drawF / totalF * 100 homeP = homeF / totalF * 100 x.append(mark) hY.append(homeP) dY.append(drawP) aY.append(awayP) awayO = awayP if awayP == 0 else 100 / awayP drawO = drawP if drawP == 0 else 100 / drawP homeO = homeP if homeP == 0 else 100 / homeP hist[mark] = (homeF, homeP) summaryWriter.writerow([ mark, totalF, '{:>4.2f}'.format(homeP), '{:>4.2f}'.format(homeO), '{:>4.2f}'.format(drawP), '{:>4.2f}'.format(drawO), '{:>4.2f}'.format(awayP), '{:>4.2f}'.format(awayO) ]) s = '' for h in sorted(hist.items(), key=lambda x: x[1][0], reverse=True): s += '{:d} ({:d} {:>5.2f}) '.format(h[0], h[1][0], h[1][1]) log.info(s) with newCSVFile('{}/{}/Stats.{}.csv'.format(analysisDir, league, model.__class__.__name__), ['Result', 'Slope', 'Intercept', 'P', 'R', 'R^2', 'Err']) \ as statsWriter: slope, intercept, r, p, stderr = stats.linregress(x, hY) r2 = r**2 log.info( 'Home: {:>4.2f} {:>4.2f} {:>4.2} {:>4.2f} {:>4.2f} {:>4.2}'. format(slope, intercept, p, r, r2, stderr)) statsWriter.writerow([ 'H', '{:>4.2f}'.format(slope), '{:>4.2f}'.format(intercept), '{:>4.2f}'.format(p), '{:>4.2f}'.format(r), '{:>4.2f}'.format(r2), '{:>4.2f}'.format(stderr) ]) slope, intercept, r, p, stderr = stats.linregress(x, dY) r2 = r**2 log.info( 'Draw: {:>4.2f} {:>4.2f} {:>4.2} {:>4.2f} {:>4.2f} {:>4.2}'. format(slope, intercept, p, r, r2, stderr)) statsWriter.writerow([ 'D', '{:>4.2f}'.format(slope), '{:>4.2f}'.format(intercept), '{:>4.2f}'.format(p), '{:>4.2f}'.format(r), '{:>4.2f}'.format(r2), '{:>4.2f}'.format(stderr) ]) slope, intercept, r, p, stderr = stats.linregress(x, aY) r2 = r**2 log.info( 'Away: {:>4.2f} {:>4.2f} {:>4.2} {:>4.2f} {:>4.2f} {:>4.2}'. format(slope, intercept, p, r, r2, stderr)) statsWriter.writerow([ 'A', '{:>4.2f}'.format(slope), '{:>4.2f}'.format(intercept), '{:>4.2f}'.format(p), '{:>4.2f}'.format(r), '{:>4.2f}'.format(r2), '{:>4.2f}'.format(stderr) ])
def analyseFixtures(resultsURLTmpl, fixturesURL, opts=sys.argv): log = Logger() (sendMail, rangeMap) = getFootyOptions(log, opts) (algoCfg, mailCfg) = getFootyConfig() rangeMap = algoCfg['rangeMap'] season = algoCfg['season'] teamErrorMap = algoCfg['teamErrorMap'] mailText = '<table border=1><tr><th>Lge</th><th>Date</th><th>HomeTeam</th><th>AwayTeam</th><th>Mark</th><th>H#</th><th>H%</th><th>H Odds</th><th>HomeTeamForm</th><th>AwayTeamForm</th></tr>' s = '{:<4s} {:<8s} {:<16s} {:<16s} {:<4s} {:s} {:<37s} {:<37s}'.format( 'Lge', 'Date', 'HomeTeam', 'AwayTeam', 'Mark', fST(('H#', 'H%', 'HO')), 'HomeTeamForm', 'AwayTeamForm') termText = '\n' + hl(s) + '\n' with newCSVFile( '{}/Betting.{}.csv'.format(analysisDir, model.__class__.__name__), [ 'Lge', 'Date', 'HomeTeam', 'AwayTeam', 'Mark', 'H#', 'H%', 'HOdds', 'HomeTeamForm', 'AwayTeamForm' ]) as bettingWriter: league = '' data = {} summaryData = {} with readCSVFileAsDict(fixturesURL) as fixturesReader: for fix in fixturesReader: log.debug(fix) ind = 'b\"Div' try: fix['b\"Div'] except: ind = 'b\'Div' if fix[ind] not in rangeMap: continue if league != fix[ind]: league = fix[ind] resultsURL = resultsURLTmpl.format(season, league) log.info(resultsURL) with readCSVFileAsDict(resultsURL) as resultsReader: data = model.processMatches(resultsReader) with readCSVFileAsDict( '{}/{}/Summary.{}.csv'.format(analysisDir, league, model.__class__.__name__)) \ as summaryReader: for summ in summaryReader: mark = int(summ['Mark']) f = int(summ['Frequency']) hP = float(summ['%H']) dP = float(summ['%D']) aP = float(summ['%A']) summaryData[mark] = { 'H': (int(f * (hP / 100)), float(summ['%H']), float(summ['HO'])), 'D': (int(f * (dP / 100)), float(summ['%D']), float(summ['DO'])), 'A': (int(f * (aP / 100)), float(summ['%A']), float(summ['AO'])) } ht = fix['HomeTeam'] if ht in teamErrorMap: ht = teamErrorMap[ht] at = fix['AwayTeam'] if at in teamErrorMap: at = teamErrorMap[at] date, ht, at, mark, hForm, aForm = model.markMatch( data, fix['Date'], ht, at) if mark is None or mark not in range(-15, 16): continue hSD = summaryData[mark]['H'] aSD = summaryData[mark]['A'] dSD = summaryData[mark]['D'] s = '{:<4s} {:<8s} {:<16s} {:<16s} {:4d} {:s} ({:s}) ({:s})'\ .format(league, date, ht, at, mark, fSD(hSD), hForm, aForm) mail_s = '<tr><td>{:s}</td><td>{:s}</td><td>{:s}</td><td>{:s}</td><td align="right">{:>4d}</td><td align="right">{:>4d}</td><td align="right">{:>6.2f}%</td><td align="right">{:>5.2f}</td><td align="right">{:s}</td><td align="right">{:s}</td></tr>'.format( league, date, ht, at, mark, hSD[0], hSD[1], hSD[2], hForm, aForm) if mark in rangeMap[league]: termText += hl(s) + '\n' mailText += mail_hl(mail_s) else: termText += s + '\n' mailText += mail_s bettingWriter.writerow((league, date, ht, at, mark, hSD[0], hSD[1], hSD[2], hForm, aForm)) log.info(termText) mailText += '</table>' mailText = 'MIME-Version: 1.0\nContent-type: text/html\nSubject: Footy Bets\n\n{}'.format( mailText) if sendMail: fromAddr = mailCfg['fromAddr'] toAddrs = mailCfg['toAddrs'] server = smtplib.SMTP(mailCfg['svr'], int(mailCfg['port'])) server.ehlo() server.starttls() server.ehlo() server.login(fromAddr, mailCfg['pwd']) server.sendmail(fromAddr, toAddrs, mailText) server.quit() log.info('email sent to: {!s}'.format(toAddrs))