import pandas as pd import os import sys import configparser from datetime import datetime, timedelta, timezone import requests

=== Загрузка конфигурации ===
base_path = os.path.dirname(sys.executable if getattr(sys, 'frozen', False) else os.path.abspath(file)) def get_config_path(): return os.path.join(base_path, "config_kelly_tg.ini")

config = configparser.ConfigParser() with open(get_config_path(), encoding='utf-8') as f: config.read_file(f)

=== Настройки из конфига ===
TELEGRAM_TOKEN = config['telegram']['token_2'] TELEGRAM_CHAT_ID = config['telegram']['chat_id']

BANK = float(config['settings']['bank']) MIN_MINUTES_BEFORE_START = int(config['settings']['min_minutes_before_start']) TIMEZONE_OFFSET = int(config['settings']['timezone_offset']) MIN_FAIR_P = float(config['settings'].get('min_fair_p2', 0.0)) MIN_MARGIN = float(config['settings'].get('min_margin', 8.0)) # Новая настройка MIN_K1 = float(config['settings'].get('min_k1', 1.59)) MAX_K1 = float(config['settings'].get('max_k1', 1.76))# Новая настройка

ALLOWED_STAKE_ON = [s.strip().lower() for s in config['settings'].get('allowed_stake_on2', 'underdog').split(',')] MIN_FAV_K = float(config['settings'].get('min_fav_k', 1.0))

=== Пути к файлам ===
MATCH_PENDING = os.path.join(base_path, 'matches_pending.csv') MATCH_SENT = os.path.join(base_path, 'match_sent2.csv') REJECTED_LOG = os.path.join(base_path, 'match_rejected_log2.csv') MATCH_RESULTS = os.path.join(base_path, 'match_results_kelly2.csv.csv')

if os.path.exists(MATCH_RESULTS): df_results = pd.read_csv(MATCH_RESULTS, sep=';', encoding='utf-8-sig') df_results.columns = df_results.columns.str.strip()

# Приведение типов
def parse_margin(value):
    try:
        return float(str(value).replace("'", "").replace(",", ".").strip())
    except:
        return None


df_results['margin'] = df_results['margin'].apply(parse_margin)
df_results['sport'] = df_results['sport'].str.strip().str.lower()
df_results['bet_result'] = df_results['bet_result'].str.strip()
else: df_results = pd.DataFrame()

def get_stats_for_match(row): if df_results.empty: return "📊 Статистика: нет данных"

sport = str(row['sport']).strip().lower()
margin = row['margin']
if margin is None:
    return "📊 Статистика: нет данных"

# Допуск ±0.5%
margin_delta = 0.00001
min_m = margin - margin_delta
max_m = margin + margin_delta

# Фильтруем
matches = df_results[
    (df_results['sport'] == sport) &
    (df_results['margin'].notna()) &
    (df_results['margin'] >= min_m) &
    (df_results['margin'] <= max_m)
]

total = len(matches)
won = len(matches[matches['bet_result'] == '✅'])
lost = len(matches[matches['bet_result'] == '❌'])
if len(matches) >= 1:
    last_id = matches.iloc[-1]['bet_result']
    if last_id == '✅':
        last_id = '❌'
    else:
        last_id = '✅'
else:
    last_id = "нет матча"
if len(matches) >= 2:
    prelast_id = matches.iloc[-2]['bet_result']
    if prelast_id == '✅':
        prelast_id = '❌'
    else:
        prelast_id = '✅'
else:
    prelast_id = "нет матча"



if total == 0:
    return "📊 Статистика: нет данных"

return (f"📊 Статистика: {lost}✅/ {won}❌ из {total} матчей ({sport.title()}\n"
        f"<b>📊 Prelast match:{prelast_id}</b>\n"
        f"<b>📊 Last match:{last_id}</b>\n")
=== Парсинг данных ===
def parse_float(value): try: return float(str(value).replace("'", "").strip()) except: return None

df = pd.read_csv(MATCH_PENDING, sep=';', encoding='utf-8-sig') df.columns = df.columns.str.strip()

df['k1'] = df['k1'].apply(parse_float) df['k2'] = df['k2'].apply(parse_float) df['margin'] = df['margin'].apply(parse_float)

=== Расчёт fair вероятностей ===
def calculate_fair_probabilities(row): try: k1 = row['k1'] k2 = row['k2'] if not k1 or not k2: return pd.Series([None, None, None]) p1 = 1 / k1 p2 = 1 / k2 s = p1 + p2 fair_p1 = p1 / s fair_p2 = p2 / s stake_on = str(row['stake_on']).strip().lower() if stake_on == 'favorite': fair_p = fair_p1 if k1 < k2 else fair_p2 chosen_k = min(k1, k2) elif stake_on == 'underdog': fair_p = fair_p2 if k2 > k1 else fair_p1 chosen_k = max(k1, k2) else: return pd.Series([None, None, None]) return pd.Series([fair_p, chosen_k, stake_on]) except: return pd.Series([None, None, None])

df[['fair_p', 'chosen_k', 'stake_on_clean']] = df.apply(calculate_fair_probabilities, axis=1)

=== Парсинг времени ===
def parse_start_time(row): val = str(row['start_time']).strip() for fmt in ("%Y-%m-%d %H:%M", "%d.%m.%Y %H:%M"): try: dt = datetime.strptime(val, fmt) local_dt = dt - timedelta(hours=TIMEZONE_OFFSET) return local_dt.replace(tzinfo=timezone.utc) except: continue return None

df['start_dt_utc'] = df.apply(parse_start_time, axis=1) now_utc = datetime.now(timezone.utc) df['minutes_to_start'] = (df['start_dt_utc'] - now_utc).dt.total_seconds() / 60

=== Функция проверки матчей ===
def rejection_reason(row): if row['fair_p'] is None or row['fair_p'] < MIN_FAIR_P: return f'fair_p < {MIN_FAIR_P:.2%}' if row['margin'] is not None and row['margin'] < MIN_MARGIN: return f'маржа ниже {MIN_MARGIN}%' if row['k1'] is not None and row['k1'] < MIN_K1: return f'коэф. k1 < {MIN_K1}' if row['k1'] is not None and row['k1'] > MAX_K1: return f'коэф. k1 < {MAX_K1}' if not (0 <= row['minutes_to_start'] <= MIN_MINUTES_BEFORE_START): return 'не входит в окно времени' if row['stake_on_clean'] not in ALLOWED_STAKE_ON: return 'stake_on не в списке допустимых' if row['stake_on_clean'] == 'favorite': fav_k = min(row['k1'], row['k2']) if fav_k < MIN_FAV_K: return f'коэф. фаворита < {MIN_FAV_K}' return None

df['rejection_reason'] = df.apply(rejection_reason, axis=1)

=== Фильтрация ===
filtered = df[df['rejection_reason'].isna()] rejected = df[df['rejection_reason'].notna()]

=== Логирование отклоненных матчей ===
if not rejected.empty: rejected.to_csv(REJECTED_LOG, sep=';', index=False, encoding='utf-8-sig') print(f"⛔ Отклонено: {len(rejected)} матчей. См. {REJECTED_LOG}")

=== Проверка отправленных матчей ===
if os.path.exists(MATCH_SENT): sent_df = pd.read_csv(MATCH_SENT, sep=';', encoding='utf-8-sig') already_sent = set(sent_df['match_id'].astype(str)) else: already_sent = set()

new_matches = filtered[~filtered['match_id'].astype(str).isin(already_sent)]

=== Функция отправки в Telegram ===
def send_to_telegram(message): url = f"https://api.telegram.org/bot{TELEGRAM_TOKEN}/sendMessage" payload = {"chat_id": TELEGRAM_CHAT_ID, "text": message, "parse_mode": "HTML"} return requests.post(url, data=payload).status_code == 200

def format_message(row): stats = get_stats_for_match(row) return ( f"<b>🎯 Ставка</b>\n" f"<b>Матч:</b> {row['team1']} vs {row['team2']}\n" f"<b>Вид спорта:</b> {row['sport']}\n" f"<b>Турнир:</b> {row['tournament']}\n" f"<b>Начало:</b> {row['start_time']} (UTC+{TIMEZONE_OFFSET})\n" f"<b>Сторона ставки:</b> СТАВИТЬ НА ФАВОРИТА\n" f"<b>Коэффициент:</b> {row['chosen_k']:.2f}\n" f"<b>Fair вероятность:</b> {row['fair_p']:.2%}\n" f"<b>Маржа:</b> {row['margin']}%\n" f"{stats}" )

=== Логика отправки ===
#send_to_telegram("💚💚💚💚 <b>Запуск парсера</b> 💚💚💚💚\n"

f"🕒Время до начала матча <i><code>{MIN_MINUTES_BEFORE_START/60}h</code></i> 🕒")
if new_matches.empty: #send_to_telegram("⚠️ <b>Матчи не найдены</b>") print("⚠️ Матчи не найдены") else: sent = [] for _, row in new_matches.iterrows(): msg = format_message(row) if send_to_telegram(msg): sent.append(row) print(f"✅ Отправлено: {row['match_id']}") else: print(f"❌ Ошибка отправки: {row['match_id']}")

if sent:
    sent_df = pd.DataFrame(sent)
    for col in ['k1', 'k2', 'margin', 'chosen_k']:
        sent_df[col] = sent_df[col].apply(lambda x: f"'{x}'" if pd.notnull(x) else x)
    sent_df.to_csv(MATCH_SENT, sep=';', index=False, mode='a', header=not os.path.exists(MATCH_SENT), encoding='utf-8-sig')
    print(f"✅ Сохранено {len(sent_df)} матчей в {MATCH_SENT}")
Надо добавить условие, чтобы отправлялись матчиь только по 'sport' Теннис