How to Automate III: Betfair Data Scientists Models
This tutorial is Part three of the How to Automate series and follows on logically from the How to Automate II tutorial we shared previously. If you're still new to Flumine we suggest you take a look at part one and part two before diving into this one. The overall goal of the How to Automate series was to learn how to use Flumine to Automate our own Betting Model. So far we have covered how Flumine works, lets now put it into action with one of Betfair's own data science models which will give us a solid foundation for part four where we automate our own model.
As always please reach out with feedback, suggestions or queries, or feel free to submit a pull request if you catch some bugs or have other improvements!
Context
Betfair’s own Data Scientists have created a few inhouse models which produce ratings that you can use as racing and sporting tips. In our previous monthly meet ups, we learnt how to Backtest these models ourselves, now we will learn how actually automate those models! We will be automating the thoroughbred ratings model and the greyhound's model, but you can easily apply this to any model and if you have a specific angle you want to automate, you can just copy & paste our code and modify it to your liking!
Scrape Today's Model Ratings
Let's quickly scrape the ratings that we want to automate, all of the Betfair Data Science models are found on the Betfair Hub Models page. If we follow the links to the Horse Ratings Model and the Greyhounds Ratings Model we find that URL links behind the ratings download buttons have a consistent URL pattern that is easy to scrape.
You end up with a link like this for the horse ratings model:
https://betfair-data-supplier-prod.herokuapp.com/api/widgets/kash-ratings-model/datasets?date=2022-03-09&presenter=RatingsPresenter&csv=true
and one like this for the greyhounds ratings model:
https://betfair-data-supplier-prod.herokuapp.com/api/widgets/iggy-joey/datasets?date=2022-03-09&presenter=RatingsPresenter&csv=true
We can take advantage of this consistency and use some simple python code to scrape all the ratings into a pandas dataframe by simply changing the date in the url.
# Thoroughbred model (named the kash-ratings-model)
kash_url_1 = 'https://betfair-data-supplier-prod.herokuapp.com/api/widgets/kash-ratings-model/datasets?date='
kash_url_2 = pd.Timestamp.now().strftime("%Y-%m-%d") # todays date formatted as YYYY-mm-dd
kash_url_3 = '&presenter=RatingsPresenter&csv=true'
kash_url = kash_url_1 + kash_url_2 + kash_url_3
kash_url
# Greyhounds model (named the iggy-joey-model)
iggy_url_1 = 'https://betfair-data-supplier-prod.herokuapp.com/api/widgets/iggy-joey/datasets?date='
iggy_url_2 = pd.Timestamp.now().strftime("%Y-%m-%d")
iggy_url_3 = '&presenter=RatingsPresenter&csv=true'
iggy_url = iggy_url_1 + iggy_url_2 + iggy_url_3
iggy_url
Now that we have the URL, that dynamically changes to the current date, let's scrape the data using pandas and do some quick cleaning. We only really need three variables, the market_id
so we know what market to bet on, the selection_id
to know which horse/dog the rating is referring to and the most important one the rating
.
# Download todays thoroughbred ratings
kash_df = pd.read_csv(kash_url)
## Data clearning
# Rename Columns
kash_df = kash_df.rename(columns={"meetings.races.bfExchangeMarketId":"market_id","meetings.races.runners.bfExchangeSelectionId":"selection_id","meetings.races.runners.ratedPrice":"rating"})
# Only keep columns we need
kash_df = kash_df[['market_id','selection_id','rating']]
# Convert market_id to string
kash_df['market_id'] = kash_df['market_id'].astype(str)
kash_df
Let's also set up the Dataframe so that we can easily get our Rating when given market_id
and selection_id
:
Let's do the same thing for the greyhound model
# Download todays greyhounds ratings
iggy_df = pd.read_csv(iggy_url)
## Data clearning
# Rename Columns
iggy_df = iggy_df.rename(columns={"meetings.races.bfExchangeMarketId":"market_id","meetings.races.runners.bfExchangeSelectionId":"selection_id","meetings.races.runners.ratedPrice":"rating"})
# Only keep columns we need
iggy_df = iggy_df[['market_id','selection_id','rating']]
# Convert market_id to string
iggy_df['market_id'] = iggy_df['market_id'].astype(str)
iggy_df
# Set market_id and selection_id as index for easy referencing
iggy_df = iggy_df.set_index(['market_id','selection_id'])
iggy_df
Automate Todays Model Ratings
Now that we have all of our ratings in a nice clean DataFrame we can easily automate them in Flumine.
Log into Flumine
# Import libraries for logging in
import betfairlightweight
from flumine import Flumine, clients
# Credentials to login and logging in
trading = betfairlightweight.APIClient('username','password',app_key='appkey')
client = clients.BetfairClient(trading, interactive_login=True)
# Login
framework = Flumine(client=client)
# Code to login when using security certificates
# trading = betfairlightweight.APIClient('username','password',app_key='appkey', certs=r'C:\Users\zhoui\openssl_certs')
# client = clients.BetfairClient(trading)
# framework = Flumine(client=client)
Create Our Strategy in Flumine
Strategy for Thoroughbreds
This is where you can come up with exciting and innovative strategies!
But because we are lame, let's start off with a simple strategy that checks prices 60 seconds before the jump and Backs anything greater than our ratings and Lays anything less than our ratings all with a flat stake size of $5
If you get confused about the code structure for how Flumine and BaseStrategy works, be sure to take a check out How to Automate I as it dives right into how to use Flumine.
Our main logic is:
- Check how far out from the jump we are
- If we are within 60 seconds from the jump check each horse's market prices
- If best Back price > model price then place a $5 Back bet
- If best Lay price < model price then place a $5 Lay bet
# Import libraries and logging
from flumine import BaseStrategy
from flumine.order.trade import Trade
from flumine.order.order import LimitOrder, OrderStatus
from flumine.markets.market import Market
from betfairlightweight.filters import streaming_market_filter
from betfairlightweight.resources import MarketBook
import pandas as pd
import logging
# Will create a file called how_to_automate_3.log in our current working directory
logging.basicConfig(filename = 'how_to_automate_3.log', level=logging.INFO, format='%(asctime)s:%(levelname)s:%(message)s')
# New strategy called FlatKashModel for the Thoroughbreds model
class FlatKashModel(BaseStrategy):
def start(self) -> None:
print("starting strategy 'FlatKashModel'")
def check_market_book(self, market: Market, market_book: MarketBook) -> bool:
# process_market_book only executed if this returns True
if market_book.status != "CLOSED":
return True
# If check_market_book returns true i.e. the market is open and not closed then we will run process_market_book once initially
# After the first inital time process_market_book has been run, every single time the market ticks, process_market_book will run again
def process_market_book(self, market: Market, market_book: MarketBook) -> None:
# If time is less than 1min and we haven't placed a bet yet then look at our ratings and place bet
if market.seconds_to_start < 60 and market_book.inplay == False:
for runner in market_book.runners:
# Check runner hasn't scratched and that first layer of back or lay price exists
if runner.status == "ACTIVE" and runner.ex.available_to_back[0] and runner.ex.available_to_lay[0]:
# If best available to back price is > rated price then flat $5 bet
if runner.ex.available_to_back[0]['price'] > kash_df.loc[market_book.market_id].loc[runner.selection_id].item():
trade = Trade(
market_id=market_book.market_id,
selection_id=runner.selection_id,
handicap=runner.handicap,
strategy=self,
)
order = trade.create_order(
side="BACK", order_type=LimitOrder(price=runner.ex.available_to_back[0]['price'], size=5.00)
)
market.place_order(order)
# If best available to lay price is < rated price then flat $5 lay
if runner.ex.available_to_lay[0]['price'] < kash_df.loc[market_book.market_id].loc[runner.selection_id].item():
trade = Trade(
market_id=market_book.market_id,
selection_id=runner.selection_id,
handicap=runner.handicap,
strategy=self,
)
order = trade.create_order(
side="LAY", order_type=LimitOrder(price=runner.ex.available_to_lay[0]['price'], size=5.00)
)
market.place_order(order)
Strategy for Greyhounds
Let's create the same strategy for the Greyhounds model. The only difference here is that when we reference the model price, we are referencing the DataFrame iggy_df instead of kash_df. (An alternate way we could do this if we wanted would be to combine both strategies together and just append the dataframes together, but lets just keep it nice and simple for now)
# New strategy called FlatIggyModel for the Greyhound model
class FlatIggyModel(BaseStrategy):
def start(self) -> None:
print("starting strategy 'FlatIggyModel'")
def check_market_book(self, market: Market, market_book: MarketBook) -> bool:
# process_market_book only executed if this returns True
if market_book.status != "CLOSED":
return True
# If check_market_book returns true i.e. the market is open and not closed then we will run process_market_book once initially
# After the first inital time process_market_book has been run, every single time the market ticks, process_market_book will run again
def process_market_book(self, market: Market, market_book: MarketBook) -> None:
# If time is less than 1min and we haven't placed a bet yet then look at our ratings and place bet
if market.seconds_to_start < 60 and market_book.inplay == False:
for runner in market_book.runners:
# Check runner hasn't scratched and that first layer of back or lay price exists
if runner.status == "ACTIVE" and runner.ex.available_to_back[0] and runner.ex.available_to_lay[0]:
# If best available to back price is > rated price then flat $5 bet
if runner.ex.available_to_back[0]['price'] > iggy_df.loc[market_book.market_id].loc[runner.selection_id].item():
trade = Trade(
market_id=market_book.market_id,
selection_id=runner.selection_id,
handicap=runner.handicap,
strategy=self,
)
order = trade.create_order(
side="BACK", order_type=LimitOrder(price=runner.ex.available_to_back[0]['price'], size=5.00)
)
market.place_order(order)
# If best available to lay price is < rated price then flat $5 lay
if runner.ex.available_to_lay[0]['price'] < iggy_df.loc[market_book.market_id].loc[runner.selection_id].item():
trade = Trade(
market_id=market_book.market_id,
selection_id=runner.selection_id,
handicap=runner.handicap,
strategy=self,
)
order = trade.create_order(
side="LAY", order_type=LimitOrder(price=runner.ex.available_to_lay[0]['price'], size=5.00)
)
market.place_order(order)
Running our strategy
Now that we have created two strategies we need to add both to the frame work along with the auto-terminate and bet logging we made in How to Automate II
thoroughbreds_strategy = FlatKashModel(
market_filter=streaming_market_filter(
event_type_ids=["7"], # Horse Racing
country_codes=["AU"], # Australian Markets
market_types=["WIN"], # Win Markets
),
max_order_exposure= 50, # Max bet sizes of $50
max_trade_count=1, # Max of trade/bet attempt per selection
max_live_trade_count=1, # Max of 1 unmatched Bet per selection
)
greyhounds_strategy = FlatIggyModel(
market_filter=streaming_market_filter(
event_type_ids=["4339"], # Greyhound Racing
country_codes=["AU"], # Australian Markets
market_types=["WIN"], # Win Markets
),
max_order_exposure= 50, # Max bet sizes of $50
max_trade_count=1, # Max of trade/bet attempt per selection
max_live_trade_count=1, # Max of 1 unmatched Bet per selection
)
framework.add_strategy(thoroughbreds_strategy) # Add horse racing strategy to our framework
framework.add_strategy(greyhounds_strategy) # Add greyhound racing strategy to our framework
# import logging
import datetime
from flumine.worker import BackgroundWorker
from flumine.events.events import TerminationEvent
# logger = logging.getLogger(__name__)
"""
Worker can be used as followed:
framework.add_worker(
BackgroundWorker(
framework,
terminate,
func_kwargs={"today_only": True, "seconds_closed": 1200},
interval=60,
start_delay=60,
)
)
This will run every 60s and will terminate
the framework if all markets starting 'today'
have been closed for at least 1200s
"""
# Function that stops automation running at the end of the day
def terminate(
context: dict, flumine, today_only: bool = True, seconds_closed: int = 600
) -> None:
"""terminate framework if no markets
live today.
"""
markets = list(flumine.markets.markets.values())
markets_today = [
m
for m in markets
if m.market_start_datetime.date() == datetime.datetime.utcnow().date()
and (
m.elapsed_seconds_closed is None
or (m.elapsed_seconds_closed and m.elapsed_seconds_closed < seconds_closed)
)
]
if today_only:
market_count = len(markets_today)
else:
market_count = len(markets)
if market_count == 0:
# logger.info("No more markets available, terminating framework")
flumine.handler_queue.put(TerminationEvent(flumine))
# Add the stopped to our framework
framework.add_worker(
BackgroundWorker(
framework,
terminate,
func_kwargs={"today_only": True, "seconds_closed": 1200},
interval=60,
start_delay=60,
)
)
import os
import csv
import logging
from flumine.controls.loggingcontrols import LoggingControl
from flumine.order.ordertype import OrderTypes
logger = logging.getLogger(__name__)
FIELDNAMES = [
"bet_id",
"strategy_name",
"market_id",
"selection_id",
"trade_id",
"date_time_placed",
"price",
"price_matched",
"size",
"size_matched",
"profit",
"side",
"elapsed_seconds_executable",
"order_status",
"market_note",
"trade_notes",
"order_notes",
]
class LiveLoggingControl(LoggingControl):
NAME = "BACKTEST_LOGGING_CONTROL"
def __init__(self, *args, **kwargs):
super(LiveLoggingControl, self).__init__(*args, **kwargs)
self._setup()
# Changed file path and checks if the file orders_hta_3.csv already exists, if it doens't then create it
def _setup(self):
if os.path.exists("orders_hta_3.csv"):
logging.info("Results file exists")
else:
with open("orders_hta_3.csv", "w") as m:
csv_writer = csv.DictWriter(m, delimiter=",", fieldnames=FIELDNAMES)
csv_writer.writeheader()
def _process_cleared_orders_meta(self, event):
orders = event.event
with open("orders_hta_3.csv", "a") as m:
for order in orders:
if order.order_type.ORDER_TYPE == OrderTypes.LIMIT:
size = order.order_type.size
else:
size = order.order_type.liability
if order.order_type.ORDER_TYPE == OrderTypes.MARKET_ON_CLOSE:
price = None
else:
price = order.order_type.price
try:
order_data = {
"bet_id": order.bet_id,
"strategy_name": order.trade.strategy,
"market_id": order.market_id,
"selection_id": order.selection_id,
"trade_id": order.trade.id,
"date_time_placed": order.responses.date_time_placed,
"price": price,
"price_matched": order.average_price_matched,
"size": size,
"size_matched": order.size_matched,
"profit": 0 if not order.cleared_order else order.cleared_order.profit,
"side": order.side,
"elapsed_seconds_executable": order.elapsed_seconds_executable,
"order_status": order.status.value,
"market_note": order.trade.market_notes,
"trade_notes": order.trade.notes_str,
"order_notes": order.notes_str,
}
csv_writer = csv.DictWriter(m, delimiter=",", fieldnames=FIELDNAMES)
csv_writer.writerow(order_data)
except Exception as e:
logger.error(
"_process_cleared_orders_meta: %s" % e,
extra={"order": order, "error": e},
)
logger.info("Orders updated", extra={"order_count": len(orders)})
def _process_cleared_markets(self, event):
cleared_markets = event.event
for cleared_market in cleared_markets.orders:
logger.info(
"Cleared market",
extra={
"market_id": cleared_market.market_id,
"bet_count": cleared_market.bet_count,
"profit": cleared_market.profit,
"commission": cleared_market.commission,
},
)
framework.add_logging_control(
LiveLoggingControl()
)
Conclusion and next steps
There we have it. We now have a bot that you can turn on at the start of a day by hitting the run all button. It will automatically scrape all the data online, place bets throughout the day and at the end of the day stop itself.
Then on the next day you can hit run all again, without needing to update anything.
While Betfair's own Data Science models are easy to automate you're also not likely to become rich. They are also available to everyone freely online, so any edge is likely already priced in.
That's why for the next part of this series we will be learning:
Complete code
Run the code from your ide by using py <filename>
.py, making sure you amend the path to point to your input data.
import pandas as pd
from flumine import BaseStrategy
from flumine.order.trade import Trade
from flumine.order.order import LimitOrder, OrderStatus
from flumine.markets.market import Market
from betfairlightweight.filters import streaming_market_filter
from betfairlightweight.resources import MarketBook
import logging
import betfairlightweight
from flumine import Flumine, clients
import datetime
from flumine.worker import BackgroundWorker
from flumine.events.events import TerminationEvent
import os
import csv
from flumine.controls.loggingcontrols import LoggingControl
from flumine.order.ordertype import OrderTypes
# Will create a file called how_to_automate_3.log in our current working directory
logging.basicConfig(filename = 'how_to_automate_3.log', level=logging.INFO, format='%(asctime)s:%(levelname)s:%(message)s')
# Thoroughbred model (named the kash-ratings-model)
kash_url_1 = 'https://betfair-data-supplier-prod.herokuapp.com/api/widgets/kash-ratings-model/datasets?date='
kash_url_2 = pd.Timestamp.now().strftime("%Y-%m-%d") # todays date formatted as YYYY-mm-dd
kash_url_3 = '&presenter=RatingsPresenter&csv=true'
kash_url = kash_url_1 + kash_url_2 + kash_url_3
kash_url
# Greyhounds model (named the iggy-joey-model)
iggy_url_1 = 'https://betfair-data-supplier-prod.herokuapp.com/api/widgets/iggy-joey/datasets?date='
iggy_url_2 = pd.Timestamp.now().strftime("%Y-%m-%d")
iggy_url_3 = '&presenter=RatingsPresenter&csv=true'
iggy_url = iggy_url_1 + iggy_url_2 + iggy_url_3
iggy_url
# Download todays thoroughbred ratings
kash_df = pd.read_csv(kash_url)
## Data clearning
# Rename Columns
kash_df = kash_df.rename(columns={"meetings.races.bfExchangeMarketId":"market_id","meetings.races.runners.bfExchangeSelectionId":"selection_id","meetings.races.runners.ratedPrice":"rating"})
# Only keep columns we need
kash_df = kash_df[['market_id','selection_id','rating']]
# Convert market_id to string
kash_df['market_id'] = kash_df['market_id'].astype(str)
kash_df
# Set market_id and selection_id as index for easy referencing
kash_df = kash_df.set_index(['market_id','selection_id'])
kash_df
# e.g. can reference like this:
# df.loc['1.195173067'].loc['4218988']
# to return 210.17
# Download todays greyhounds ratings
iggy_df = pd.read_csv(iggy_url)
## Data clearning
# Rename Columns
iggy_df = iggy_df.rename(columns={"meetings.races.bfExchangeMarketId":"market_id","meetings.races.runners.bfExchangeSelectionId":"selection_id","meetings.races.runners.ratedPrice":"rating"})
# Only keep columns we need
iggy_df = iggy_df[['market_id','selection_id','rating']]
# Convert market_id to string
iggy_df['market_id'] = iggy_df['market_id'].astype(str)
iggy_df
# Set market_id and selection_id as index for easy referencing
iggy_df = iggy_df.set_index(['market_id','selection_id'])
iggy_df
# Import libraries for logging in
import betfairlightweight
from flumine import Flumine, clients
# Credentials to login and logging in
trading = betfairlightweight.APIClient('username','password',app_key='appkey')
client = clients.BetfairClient(trading, interactive_login=True)
# Login
framework = Flumine(client=client)
# Code to login when using security certificates
# trading = betfairlightweight.APIClient('username','password',app_key='appkey', certs=r'C:\Users\zhoui\openssl_certs')
# client = clients.BetfairClient(trading)
# framework = Flumine(client=client)
# Will create a file called how_to_automate_3.log in our current working directory
logging.basicConfig(filename = 'how_to_automate_3.log', level=logging.INFO, format='%(asctime)s:%(levelname)s:%(message)s')
# New strategy called FlatKashModel for the Thoroughbreds model
class FlatKashModel(BaseStrategy):
def start(self) -> None:
print("starting strategy 'FlatKashModel'")
def check_market_book(self, market: Market, market_book: MarketBook) -> bool:
# process_market_book only executed if this returns True
if market_book.status != "CLOSED":
return True
# If check_market_book returns true i.e. the market is open and not closed then we will run process_market_book once initially
# After the first inital time process_market_book has been run, every single time the market ticks, process_market_book will run again
def process_market_book(self, market: Market, market_book: MarketBook) -> None:
# If time is less than 1min and we haven't placed a bet yet then look at our ratings and place bet
if market.seconds_to_start < 60 and market_book.inplay == False:
for runner in market_book.runners:
# Check runner hasn't scratched and that first layer of back or lay price exists
if runner.status == "ACTIVE" and runner.ex.available_to_back[0] and runner.ex.available_to_lay[0]:
# If best available to back price is > rated price then flat $5 bet
if runner.ex.available_to_back[0]['price'] > kash_df.loc[market_book.market_id].loc[runner.selection_id].item():
trade = Trade(
market_id=market_book.market_id,
selection_id=runner.selection_id,
handicap=runner.handicap,
strategy=self,
)
order = trade.create_order(
side="BACK", order_type=LimitOrder(price=runner.ex.available_to_back[0]['price'], size=5.00)
)
market.place_order(order)
# If best available to lay price is < rated price then flat $5 lay
if runner.ex.available_to_lay[0]['price'] < kash_df.loc[market_book.market_id].loc[runner.selection_id].item():
trade = Trade(
market_id=market_book.market_id,
selection_id=runner.selection_id,
handicap=runner.handicap,
strategy=self,
)
order = trade.create_order(
side="LAY", order_type=LimitOrder(price=runner.ex.available_to_lay[0]['price'], size=5.00)
)
market.place_order(order)
# New strategy called FlatIggyModel for the Greyhound model
class FlatIggyModel(BaseStrategy):
def start(self) -> None:
print("starting strategy 'FlatIggyModel'")
def check_market_book(self, market: Market, market_book: MarketBook) -> bool:
# process_market_book only executed if this returns True
if market_book.status != "CLOSED":
return True
# If check_market_book returns true i.e. the market is open and not closed then we will run process_market_book once initially
# After the first inital time process_market_book has been run, every single time the market ticks, process_market_book will run again
def process_market_book(self, market: Market, market_book: MarketBook) -> None:
# If time is less than 1min and we haven't placed a bet yet then look at our ratings and place bet
if market.seconds_to_start < 60 and market_book.inplay == False:
for runner in market_book.runners:
# Check runner hasn't scratched and that first layer of back or lay price exists
if runner.status == "ACTIVE" and runner.ex.available_to_back[0] and runner.ex.available_to_lay[0]:
# If best available to back price is > rated price then flat $5 bet
if runner.ex.available_to_back[0]['price'] > iggy_df.loc[market_book.market_id].loc[runner.selection_id].item():
trade = Trade(
market_id=market_book.market_id,
selection_id=runner.selection_id,
handicap=runner.handicap,
strategy=self,
)
order = trade.create_order(
side="BACK", order_type=LimitOrder(price=runner.ex.available_to_back[0]['price'], size=5.00)
)
market.place_order(order)
# If best available to lay price is < rated price then flat $5 lay
if runner.ex.available_to_lay[0]['price'] < iggy_df.loc[market_book.market_id].loc[runner.selection_id].item():
trade = Trade(
market_id=market_book.market_id,
selection_id=runner.selection_id,
handicap=runner.handicap,
strategy=self,
)
order = trade.create_order(
side="LAY", order_type=LimitOrder(price=runner.ex.available_to_lay[0]['price'], size=5.00)
)
market.place_order(order)
logger = logging.getLogger(__name__)
"""
Worker can be used as followed:
framework.add_worker(
BackgroundWorker(
framework,
terminate,
func_kwargs={"today_only": True, "seconds_closed": 1200},
interval=60,
start_delay=60,
)
)
This will run every 60s and will terminate
the framework if all markets starting 'today'
have been closed for at least 1200s
"""
# Function that stops automation running at the end of the day
def terminate(
context: dict, flumine, today_only: bool = True, seconds_closed: int = 600
) -> None:
"""terminate framework if no markets
live today.
"""
markets = list(flumine.markets.markets.values())
markets_today = [
m
for m in markets
if m.market_start_datetime.date() == datetime.datetime.utcnow().date()
and (
m.elapsed_seconds_closed is None
or (m.elapsed_seconds_closed and m.elapsed_seconds_closed < seconds_closed)
)
]
if today_only:
market_count = len(markets_today)
else:
market_count = len(markets)
if market_count == 0:
# logger.info("No more markets available, terminating framework")
flumine.handler_queue.put(TerminationEvent(flumine))
logger = logging.getLogger(__name__)
FIELDNAMES = [
"bet_id",
"strategy_name",
"market_id",
"selection_id",
"trade_id",
"date_time_placed",
"price",
"price_matched",
"size",
"size_matched",
"profit",
"side",
"elapsed_seconds_executable",
"order_status",
"market_note",
"trade_notes",
"order_notes",
]
class LiveLoggingControl(LoggingControl):
NAME = "BACKTEST_LOGGING_CONTROL"
def __init__(self, *args, **kwargs):
super(LiveLoggingControl, self).__init__(*args, **kwargs)
self._setup()
# Changed file path and checks if the file orders_hta_2.csv already exists, if it doens't then create it
def _setup(self):
if os.path.exists("orders_hta_3.csv"):
logging.info("Results file exists")
else:
with open("orders_hta_3.csv", "w") as m:
csv_writer = csv.DictWriter(m, delimiter=",", fieldnames=FIELDNAMES)
csv_writer.writeheader()
def _process_cleared_orders_meta(self, event):
orders = event.event
with open("orders_hta_3.csv", "a") as m:
for order in orders:
if order.order_type.ORDER_TYPE == OrderTypes.LIMIT:
size = order.order_type.size
else:
size = order.order_type.liability
if order.order_type.ORDER_TYPE == OrderTypes.MARKET_ON_CLOSE:
price = None
else:
price = order.order_type.price
try:
order_data = {
"bet_id": order.bet_id,
"strategy_name": order.trade.strategy,
"market_id": order.market_id,
"selection_id": order.selection_id,
"trade_id": order.trade.id,
"date_time_placed": order.responses.date_time_placed,
"price": price,
"price_matched": order.average_price_matched,
"size": size,
"size_matched": order.size_matched,
"profit": 0 if not order.cleared_order else order.cleared_order.profit,
"side": order.side,
"elapsed_seconds_executable": order.elapsed_seconds_executable,
"order_status": order.status.value,
"market_note": order.trade.market_notes,
"trade_notes": order.trade.notes_str,
"order_notes": order.notes_str,
}
csv_writer = csv.DictWriter(m, delimiter=",", fieldnames=FIELDNAMES)
csv_writer.writerow(order_data)
except Exception as e:
logger.error(
"_process_cleared_orders_meta: %s" % e,
extra={"order": order, "error": e},
)
logger.info("Orders updated", extra={"order_count": len(orders)})
def _process_cleared_markets(self, event):
cleared_markets = event.event
for cleared_market in cleared_markets.orders:
logger.info(
"Cleared market",
extra={
"market_id": cleared_market.market_id,
"bet_count": cleared_market.bet_count,
"profit": cleared_market.profit,
"commission": cleared_market.commission,
},
)
thoroughbreds_strategy = FlatKashModel(
market_filter=streaming_market_filter(
event_type_ids=["7"], # Horse Racing
country_codes=["AU"], # Australian Markets
market_types=["WIN"], # Win Markets
),
max_order_exposure= 50, # Max bet sizes of $50
max_trade_count=1, # Max of trade/bet attempt per selection
max_live_trade_count=1, # Max of 1 unmatched Bet per selection
)
greyhounds_strategy = FlatIggyModel(
market_filter=streaming_market_filter(
event_type_ids=["4339"], # Greyhound Racing
country_codes=["AU"], # Australian Markets
market_types=["WIN"], # Win Markets
),
max_order_exposure= 50, # Max bet sizes of $50
max_trade_count=1, # Max of trade/bet attempt per selection
max_live_trade_count=1, # Max of 1 unmatched Bet per selection
)
framework.add_strategy(thoroughbreds_strategy) # Add horse racing strategy to our framework
framework.add_strategy(greyhounds_strategy) # Add greyhound racing strategy to our framework
# Add the stopped to our framework
framework.add_worker(
BackgroundWorker(
framework,
terminate,
func_kwargs={"today_only": True, "seconds_closed": 1200},
interval=60,
start_delay=60,
)
)
framework.add_logging_control(
LiveLoggingControl()
)
framework.run() # run all our strategies
Disclaimer
Note that whilst models and automated strategies are fun and rewarding to create, we can't promise that your model or betting strategy will be profitable, and we make no representations in relation to the code shared or information on this page. If you're using this code or implementing your own strategies, you do so entirely at your own risk and you are responsible for any winnings/losses incurred. Under no circumstances will Betfair be liable for any loss or damage you suffer.