Example #1
0
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()
Example #2
0
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))
Example #3
0
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)
            ])
Example #4
0
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))