import streamlit as st
import pandas as pd
import requests
# === USER CONFIGURABLE SETTINGS ===
API_KEY = st.text_input("Torn API Key", value="gZWmfZJgegZFL2vN")
TRAVEL_TYPE = st.selectbox("Travel Type", ["Standard", "Airstrip", "WLT", "Business"], index=1)
ITEM_SLOTS = st.number_input("Item Slots", min_value=1, max_value=100, value=29)
HOURS_PER_DAY = st.number_input("Hours Per Day", min_value=1, max_value=24, value=12)
TORNEX_USER_IDS = st.text_area(
"TornExchange User IDs (name:id, one per line)",
value="Malvoiy:2290370"
)
# Parse user IDs
TORNEX_USER_IDS = {
line.split(":")[0].strip(): int(line.split(":")[1].strip())
for line in TORNEX_USER_IDS.strip().splitlines() if ":" in line
}
# Travel time data in minutes (one-way)
travel_data = {
"Mexico": {"Standard": 26, "Airstrip": 18, "WLT": 13, "Business": 8, "Cost": 6500},
"Cayman Islands": {"Standard": 35, "Airstrip": 25, "WLT": 18, "Business": 11, "Cost": 10000},
"Canada": {"Standard": 41, "Airstrip": 29, "WLT": 20, "Business": 12, "Cost": 9000},
"Hawaii": {"Standard": 134, "Airstrip": 94, "WLT": 67, "Business": 40, "Cost": 11000},
"United Kingdom": {"Standard": 159, "Airstrip": 111, "WLT": 80, "Business": 48, "Cost": 18000},
"Argentina": {"Standard": 167, "Airstrip": 117, "WLT": 83, "Business": 50, "Cost": 21000},
"Switzerland": {"Standard": 175, "Airstrip": 123, "WLT": 88, "Business": 53, "Cost": 27000},
"Japan": {"Standard": 225, "Airstrip": 158, "WLT": 113, "Business": 68, "Cost": 32000},
"China": {"Standard": 242, "Airstrip": 169, "WLT": 121, "Business": 72, "Cost": 35000},
"United Arab Emirates": {"Standard": 271, "Airstrip": 190, "WLT": 135, "Business": 81, "Cost": 32000},
"South Africa": {"Standard": 297, "Airstrip": 208, "WLT": 149, "Business": 89, "Cost": 40000}
}
foreign_prices = {
"South Africa": {"Xanax": 752508, "LSD": 33445, "Lion Plushie": 400, "African Violet": 2000, "Opium": 66890, "PCP": 100334, "Shrooms": 16720},
"United Arab Emirates": {"Camel Plushie": 14000, "Tribulus Omanense": 6000},
"China": {"Panda Plushie": 400, "Peony": 5000, "Ecstasy": 45840, "LSD": 35597, "Speed": 152801, "Opium": 72632, "PCP": 103619},
"Argentina": {"Monkey Plushie": 400, "Ceibo Flower": 500, "Cannabis": 5000, "LSD": 41210, "Speed": 168292, "Ketamine": 100005, "Shrooms": 19801},
"Japan": {"Cherry Blossom": 500, "Xanax": 819084, "Ecstasy": 46424, "Speed": 154716, "Opium": 73543, "Ketamine": 91929, "Shrooms": 18386, "Vicodin": 119507},
"United Kingdom": {"Heather": 5000, "Red Fox Plushie": 1000, "Nessie Plushie": 200, "Cannabis": 5057, "Ecstasy": 50063, "PCP": 121365, "Ketamine": 100127, "Shrooms": 20228, "Vicodin": 132794, "Xanax": 910149},
"Mexico": {"Jaguar Plushie": 10000, "Dahlia": 300},
"Cayman Islands": {"Stingray Plushie": 400, "Banana Orchid": 4000},
"Canada": {"Wolverine Plushie": 30, "Crocus": 600, "Ecstasy": 61002, "Cannabis": 6099, "PCP": 146389, "Vicodin": 165029, "Xanax": 1109126},
"Hawaii": {"Orchid": 700},
"Switzerland": {"Chamois Plushie": 400, "Edelweiss": 900, "Cannabis": 4936, "LSD": 39487, "Speed": 167818, "PCP": 118460, "Ketamine": 100701, "Shrooms": 19743}
}
item_name_to_id = {
"Cannabis": 196, "Ecstasy": 197, "Ketamine": 198, "LSD": 199, "Opium": 200,
"PCP": 201, "Shrooms": 203, "Speed": 204, "Vicodin": 205, "Xanax": 206,
"Jaguar Plushie": 258, "Dahlia": 260, "Wolverine Plushie": 261, "Crocus": 263, "Orchid": 264,
"Nessie Plushie": 266, "Heather": 267, "Red Fox Plushie": 268, "Monkey Plushie": 269,
"Ceibo Flower": 271, "Edelweiss": 272, "Chamois Plushie": 273, "Panda Plushie": 274,
"Peony": 276, "Cherry Blossom": 277, "Lion Plushie": 281, "African Violet": 282,
"Camel Plushie": 384, "Tribulus Omanense": 385, "Banana Orchid": 617, "Stingray Plushie": 618
}
def fetch_market_price(item_id):
url = f"https://api.torn.com/v2/market/{item_id}/itemmarket?offset=0&key={API_KEY}"
try:
response = requests.get(url)
data = response.json()
return data.get("itemmarket", {}).get("item", {}).get("average_price")
except Exception as e:
st.warning(f"❌ Failed to fetch price for item ID {item_id}: {e}")
return None
def fetch_tornexchange_user_price(user_id, item_id):
url = f"https://tornexchange.com/api/price?user_id={user_id}&item_id={item_id}"
try:
response = requests.get(url)
data = response.json()
if data.get("status") == "error" and data.get("rate_limited"):
retry_after = data.get("retry_after", 0)
mins, secs = divmod(retry_after, 60)
st.error(f"TornEx API Request limit reached. Try again in {mins} minutes {secs} seconds.")
st.stop()
if data.get("status") == "success":
return data["data"]["price"]
except Exception as e:
st.warning(f"❌ Failed to fetch TornExchange price for user {user_id}, item {item_id}: {e}")
return None
def fetch_tornexchange_best_listing(item_id):
url = f"https://tornexchange.com/api/listings?item_id={item_id}&sort_by=price&page=1"
try:
response = requests.get(url)
data = response.json()
if data.get("status") == "error" and data.get("rate_limited"):
retry_after = data.get("retry_after", 0)
mins, secs = divmod(retry_after, 60)
st.error(f"TornEx API Request limit reached. Try again in {mins} minutes {secs} seconds.")
st.stop()
if data.get("status") == "success":
listings = data.get("data", {}).get("listings", [])
if listings:
return listings[0]["price"], listings[0]["trader"]
except Exception as e:
st.warning(f"❌ Failed to fetch TornExchange best listing for item {item_id}: {e}")
return None, None
if st.button("Run Profit Analysis"):
results = []
for country, items in foreign_prices.items():
travel_time = travel_data[country][TRAVEL_TYPE]
round_trip = travel_time * 2
trips_per_day = (HOURS_PER_DAY * 60) // round_trip
for item_name, abroad_price in items.items():
item_id = item_name_to_id.get(item_name)
if not item_id:
st.warning(f"⚠️ Missing item ID for {item_name}")
continue
market_price = fetch_market_price(item_id)
if not market_price:
st.warning(f"⚠️ Could not fetch price for {item_name} (ID {item_id})")
continue
# Apply 5% tax to market price
taxed_market_price = market_price * 0.95
# TornExchange best listing (now using /listings and highest price)
best_listing_price, best_listing_trader = fetch_tornexchange_best_listing(item_id)
# TornExchange user prices
user_prices = {}
for user, user_id in TORNEX_USER_IDS.items():
user_price = fetch_tornexchange_user_price(user_id, item_id)
if user_price:
user_prices[user] = user_price
# Profit calculations
profit_per_item_market = taxed_market_price - abroad_price
profit_per_trip_market = profit_per_item_market * ITEM_SLOTS
profit_per_minute_market = profit_per_trip_market / round_trip
profit_per_day_market = profit_per_trip_market * trips_per_day
# TornExchange best listing profit
if best_listing_price:
profit_per_item_best_listing = best_listing_price - abroad_price
profit_per_trip_best_listing = profit_per_item_best_listing * ITEM_SLOTS
profit_per_minute_best_listing = profit_per_trip_best_listing / round_trip
else:
profit_per_item_best_listing = profit_per_trip_best_listing = profit_per_minute_best_listing = None
# TornExchange user profits
user_profits = {}
for user, price in user_prices.items():
profit_per_item_user = price - abroad_price
profit_per_trip_user = profit_per_item_user * ITEM_SLOTS
profit_per_minute_user = profit_per_trip_user / round_trip
user_profits[user] = {
"price": price,
"profit_per_item": profit_per_item_user,
"profit_per_trip": profit_per_trip_user,
"profit_per_minute": profit_per_minute_user
}
# Decide which is best
best_method = "Market"
best_profit_per_minute = profit_per_minute_market
best_sell_price = round(taxed_market_price, 2)
best_sell_to = "Market"
if best_listing_price and profit_per_minute_best_listing and profit_per_minute_best_listing > best_profit_per_minute:
best_method = "TornExchange Best"
best_profit_per_minute = profit_per_minute_best_listing
best_sell_price = best_listing_price
best_sell_to = f"TornExchange ({best_listing_trader})"
for user, profit_data in user_profits.items():
if profit_data["profit_per_minute"] and profit_data["profit_per_minute"] > best_profit_per_minute:
best_method = f"TornExchange User ({user})"
best_profit_per_minute = profit_data["profit_per_minute"]
best_sell_price = profit_data["price"]
best_sell_to = f"TornExchange ({user})"
results.append({
"Destination": country,
"Item": item_name,
"Abroad Price": abroad_price,
"Taxed Market Price": round(taxed_market_price, 2),
"TornEx Best Price": best_listing_price,
"TornEx Best Trader": best_listing_trader,
**{f"TornEx {user} Price": profit_data["price"] for user, profit_data in user_profits.items()},
"Best Sell Method": best_method,
"Best Sell Price": best_sell_price,
"Best Sell To": best_sell_to,
"Profit/min Market": round(profit_per_minute_market, 2) if profit_per_minute_market is not None else None,
"Profit/min TornEx Best": round(profit_per_minute_best_listing, 2) if profit_per_minute_best_listing is not None else None,
**{f"Profit/min TornEx {user}": round(profit_data['profit_per_minute'], 2) for user, profit_data in user_profits.items()},
"Best Profit/min": round(best_profit_per_minute, 2) if best_profit_per_minute is not None else None,
})
df = pd.DataFrame(results)
df["Destination"] = df["Destination"].replace("United Arab Emirates", "UAE")
if df.empty:
st.error("🚫 No results. Check API key or internet connection.")
else:
df = df.sort_values(by="Best Profit/min", ascending=False).reset_index(drop=True)
st.subheader("Top 10 Profitable Routes (Market, TornExchange)")
main_cols = [
"Destination",
"Item",
"Taxed Market Price",
"Profit/min Market",
"TornEx Best Price",
"TornEx Best Trader",
"Profit/min TornEx Best"
]
main_df = df[main_cols].head(10)
main_df = main_df.rename(columns={
"Taxed Market Price": "Taxed Price",
"Profit/min Market": "Taxed PPM",
"TornEx Best Price": "TornEx Best Price",
"TornEx Best Trader": "TornEx Best Trader",
"Profit/min TornEx Best": "TornEx PPM"
})
st.dataframe(main_df)
for user in TORNEX_USER_IDS:
st.markdown(f"#### TornEx {user} Prices and PPM")
user_cols = [
"Item",
f"TornEx {user} Price",
f"Profit/min TornEx {user}"
]
user_df = df[user_cols].head(10)
user_df = user_df.rename(columns={
f"TornEx {user} Price": f"{user} Price",
f"Profit/min TornEx {user}": f"{user} PPM"
})
st.dataframe(user_df)
st.markdown("#### Profit per day (Market, TornEx Best, and Users)")
profit_day_cols = [
"Item",
"Profit/min Market",
"Profit/min TornEx Best",
*[f"Profit/min TornEx {user}" for user in TORNEX_USER_IDS]
]
profit_day_df = df[profit_day_cols].head(10).copy()
profit_day_df["Profit/day Market"] = profit_day_df["Profit/min Market"] * 60 * HOURS_PER_DAY
profit_day_df["Profit/day TornEx Best"] = profit_day_df["Profit/min TornEx Best"] * 60 * HOURS_PER_DAY
for user in TORNEX_USER_IDS:
profit_day_df[f"Profit/day {user}"] = profit_day_df[f"Profit/min TornEx {user}"] * 60 * HOURS_PER_DAY
show_cols = ["Item", "Profit/day Market", "Profit/day TornEx Best"] + [f"Profit/day {user}" for user in TORNEX_USER_IDS]
st.dataframe(profit_day_df[show_cols])