csgo_investment / app.py
CodemanVan
fix bug online version 1.0
23a7434
import functools
from pathlib import Path
from api import Goods, Inventory, test_tokens
import streamlit as st
from st_aggrid import AgGrid
from st_aggrid import DataReturnMode, GridUpdateMode
from st_aggrid.shared import JsCode
from st_aggrid.grid_options_builder import GridOptionsBuilder
import pandas as pd
from pyecharts.charts import Bar, Pie
from pyecharts import options as opts
from pyecharts.globals import ThemeType
import streamlit_echarts
import json
def delete_goods(inventory, index):
for i in index:
inventory.delete(i['库存编号'])
def sell_goods(inventory, index):
for i in index:
try:
inventory()[i['库存编号']].sell(eval(i['卖出价格']))
except:
st.error("卖出价格输入错误,请检查输入")
def lease_goods(inventory, index):
for i in index:
inventory()[i['库存编号']].lease()
def back_goods(inventory, index):
for i in index:
inventory()[i['库存编号']].back()
def open_inventory(path):
if type(path) == str:
st.session_state.inventory=Inventory(path)
st.session_state.inventory.save()
else:
with st.spinner("加载库存中..."):
st.session_state.inventory = Inventory(path.name)
st.success("库存已打开 ✅")
with open(st.session_state.inventory.path,'wb+') as file:
file.write(path.getvalue())
with st.spinner("更新饰品信息..."):
progress_bar = st.progress(0)
if len(st.session_state.inventory())<=0:
rate=1
else:
rate = 1 / len(st.session_state.inventory())
for p, i in enumerate(st.session_state.inventory):
progress_bar.progress(rate * p)
st.session_state.inventory()[i].refresh()
progress_bar.empty()
def save_inventory(path):
with st.spinner("保存库存中..."):
st.session_state.inventory.save()
st.success("库存保存成功 ✅")
with st.sidebar:
with open(st.session_state.inventory.path, 'rb') as f:
st.download_button('下载到本地', f, file_name=st.session_state.inventory.path)
def update_token(token):
if not test_tokens(token):
st.error("Token无效,请重新登陆悠悠,按F12查找token,格式为 Bearer xxx")
return
USER_TOKEN=token
if 'inventory' in st.session_state:
for good in st.session_state.inventory():
st.session_state.inventory()[good].token=USER_TOKEN
st.success("Token更新成功,请及时保存!!✅")
cellsytle_jscode = JsCode(
"""
function (params) {
if (params.value < 0) {
return {
'color': 'white',
'backgroundColor': 'forestgreen'
}
} else {
return {
'color': 'white',
'backgroundColor': 'crimson'
}
}
};
"""
)
def import_from_file(path,token):
if not "inventory" in st.session_state:
st.error("请新建仓库在导入数据")
return
if not test_tokens(token):
st.error("悠悠token不正确")
return
try:
if path.name.endswith('.csv'):
data=pd.read_csv(path.getvalue())
elif path.name.endswith('.xlsx'):
data=pd.read_excel(path.getvalue())
else:
st.error('%s 文件类型不支持'%path.name)
return
for i in range(data.shape[0]):
tmp = Goods(str(data.iloc[i]['Buff id']), int(data.iloc[i]['购入花费(元)']),token=token)
tmp.refresh()
st.session_state.inventory.add(tmp)
st.success(tmp.name + "已添加 ✅")
except:
st.error('检查%s中是否包含 <Buff id> <购入花费(元)>字段'%path)
def main() -> None:
global USER_TOKEN
st.header("CSGO 饰品投资追踪 :moneybag: :dollar: :bar_chart:")
st.caption("Made by Shevon & Lishuai, maintained by whatcoldwind")
st.text("请在左侧打开库存文件")
with st.sidebar:
st.subheader("选择库存")
path = st.file_uploader("上传本地库存文件")
if path:
launch = st.button('打开库存', on_click=open_inventory, args=(path,))
new_name=st.text_input("输入新建库存的名称",value="xxxxx")
if new_name:
new_one = st.button('新建库存', on_click=open_inventory, args=(new_name+'.pkl',))
save = st.button('保存库存更改', on_click=save_inventory, args=(path,))
token_value = st.text_input("token值", value=USER_TOKEN)
token = st.button('更新悠悠有品token', on_click=update_token, args=(token_value,))
if 'inventory' in st.session_state:
import_file_path = st.file_uploader("上传导出表格文件并导入到当前仓库 *.csv *.xlsx")
if import_file_path:
import_bt = st.button('导入', on_click=import_from_file, args=(import_file_path,USER_TOKEN))
st.caption('目前已启动库存 ' + st.session_state.inventory.path)
st.subheader("添加饰品")
form_track = st.form(key="track")
with form_track:
code = st.text_input("请输入饰品buff代码")
cost = eval(st.text_input("请输入购买价格,0表示仅观望", "0"))
submitted = st.form_submit_button(label="添加")
if submitted:
with st.spinner("加载饰品信息..."):
try:
tmp = Goods(code, cost,token=USER_TOKEN)
tmp.refresh()
st.session_state.inventory.add(tmp)
st.success(tmp.name + "已添加 ✅")
except:
st.error("饰品信息加载失败,请检查代码和token是否正确")
if 'inventory' in st.session_state:
for good in st.session_state.inventory():
validation=test_tokens(st.session_state.inventory()[good].token)
if validation :
USER_TOKEN=st.session_state.inventory()[good].token
st.success("✅ 你当前token有效:%s"%USER_TOKEN)
break
st.subheader("投资信息")
if len(st.session_state.inventory()) > 0:
col = st.columns(4)
col2 = st.columns(4)
col3 = st.columns(4)
col[0].metric(
"总投资额", value=f"{st.session_state.inventory.total_cost():.2f} 元",
help="购买饰品总花费"
)
col[1].metric("追踪总量", value=f"{len(st.session_state.inventory())} 件",help="加入库存文件的饰品数量")
col[2].metric(
"库存价值(Buff计,含租出)",
value=f"{st.session_state.inventory.calc_price():.2f} 元",
help="库存饰品和已租出饰品总价值"
)
col[3].metric(
"总套现", value=f"{st.session_state.inventory.sell_price():.2f} 元",
help="卖出饰品总收入"
)
earn = (
st.session_state.inventory.calc_price()
+ st.session_state.inventory.sell_price()
- st.session_state.inventory.total_cost()
)
col2[0].metric("盈利(Buff计)", value=f"{earn:.2f} 元",
help="总套现 + 库存价值 - 总投资额")
col2[1].metric(
"总收益率",
value=f"{earn/st.session_state.inventory.total_cost()*100:.2f} %",
help="盈利 / 总投资额 * 100"
)
yyyp_earn = (
st.session_state.inventory.calc_yyyp_price()
+ st.session_state.inventory.sell_price()
- st.session_state.inventory.total_cost()
)
col2[2].metric("盈利(悠悠有品计)", value=f"{yyyp_earn:.2f} 元")
col2[3].metric(
"总收益率",
value=f"{yyyp_earn/st.session_state.inventory.total_cost()*100:.2f} %",
help="盈利 / 总投资额 * 100"
)
col3[0].metric(
"持有饰品收益(Buff计)",
value=f"{st.session_state.inventory.calc_price() - st.session_state.inventory.total_cost_in_inventory():.2f} 元",
help="库存价值 - 库存内和已租出饰品总花费"
)
col3[1].metric(
"持有饰品收益率(Buff计)",
value=f"{100 * (st.session_state.inventory.calc_price() - st.session_state.inventory.total_cost_in_inventory())/st.session_state.inventory.total_cost_in_inventory():.2f} %",
help="( 持有饰品收益 - 库存内和已租出饰品总花费 ) * 100"
)
col3[2].metric(
"持有饰品收益(悠悠有品计)",
value=f"{st.session_state.inventory.calc_yyyp_price() - st.session_state.inventory.total_cost_in_inventory():.2f} 元",
help="库存价值 - 库存内和已租出饰品总花费"
)
col3[3].metric(
"持有饰品收益率(悠悠有品计)",
value=f"{100 * (st.session_state.inventory.calc_yyyp_price() - st.session_state.inventory.total_cost_in_inventory())/st.session_state.inventory.total_cost_in_inventory():.2f} %",
help="( 持有饰品收益 - 库存内和已租出饰品总花费 ) * 100"
)
st.subheader("目前资金组成")
col4 = st.columns(2)
with col4[0]:
fig1 = Pie(init_opts=opts.InitOpts(theme=ThemeType.MACARONS)).add(
"库存资金组成",
[('出租',sum(
[
st.session_state.inventory()[good].price
for good in st.session_state.inventory()
if st.session_state.inventory()[good].status == 1
]
)),
('在库',sum(
[
st.session_state.inventory()[good].price
for good in st.session_state.inventory()
if (
st.session_state.inventory()[good].status == 0
and st.session_state.inventory()[good].cost != 0
)
]
))],
radius=["30%", "75%"],
)
streamlit_echarts.st_pyecharts(fig1, height="400px", key="fig1")
with col4[1]:
fig2 = Pie(init_opts=opts.InitOpts(theme=ThemeType.MACARONS)).add(
"盈利资金组成",
[('库存增值',st.session_state.inventory.calc_price()
- st.session_state.inventory.total_cost_in_inventory(),), ('卖出收益',st.session_state.inventory.sell_earn(),)],
radius=["30%", "75%"],
)
streamlit_echarts.st_pyecharts(fig2, height="400px", key="fig2")
else:
st.caption("当前库存为空")
# 追踪列表
st.subheader("追踪列表")
st.text("右键表格可以导出表格")
if len(st.session_state.inventory()) > 0:
data = pd.DataFrame(
columns=['库存编号', 'Buff id', '名称', '状态', '购入花费(元)', '卖出价格']
)
for xx in st.session_state.inventory:
xx = st.session_state.inventory()[xx]
data.loc[len(data)] = [
xx.index,
xx.id,
xx.name,
xx.get_status(),
xx.cost,
xx.sell_price,
]
gb = GridOptionsBuilder.from_dataframe(data)
gb.configure_selection(
selection_mode='multiple',
use_checkbox=True,
)
gb.configure_side_bar()
gb.configure_grid_options(domLayout='normal')
gridOptions = gb.build()
grid = AgGrid(
data,
gridOptions=gridOptions,
allow_unsafe_jscode=True,
return_mode_value=DataReturnMode.FILTERED,
update_mode=GridUpdateMode.MODEL_CHANGED,
enable_enterprise_modules=True,
)
selected = grid["selected_rows"]
if selected != []:
print(selected)
st.button(
'删除选中饰品',
on_click=delete_goods,
args=(st.session_state.inventory, selected),
)
st.button(
'出售选中饰品',
on_click=sell_goods,
args=(st.session_state.inventory, selected),
)
st.button(
'租出选中饰品',
on_click=lease_goods,
args=(st.session_state.inventory, selected),
)
st.button(
'回仓选中饰品',
on_click=back_goods,
args=(st.session_state.inventory, selected),
)
else:
st.caption("暂无饰品记录")
goods = [st.session_state.inventory()[xx] for xx in st.session_state.inventory]
# 已购列表
st.subheader("已购列表")
st.text("右键表格可以导出表格")
track = [xx for xx in goods if xx.cost != 0]
if len(track) > 0:
data_track = pd.DataFrame([xx() for xx in track])
data_track['Status'] = data_track['Status'].map(
{0: '在库中', 1: '已租出', 2: '已卖出'}
)
data_track.columns = [
'Buff id',
'有品 id',
'名称',
'购入花费(元)',
'Buff 价格',
'有品价格',
'Steam 价格(元)',
'状态',
'有品在售',
'有品在租',
'短租价格(元)',
'长租价格(元)',
'押金(元)',
'租售比',
'理论目前收益(元)',
'理论目前收益率(%)',
'租金比例(%)',
'押金比例(%)',
'年化短租比例(%)',
'年化长租比例(%)',
'套现比例(%)',
'buff和有品价格比例',
]
data_track = data_track.round(4)
#del data_track['Buff id']
#del data_track['有品 id']
gb0 = GridOptionsBuilder.from_dataframe(data_track)
gb0.configure_columns(["Buff id", "有品 id", "名称"], pinned=True)
gb0.configure_columns(
['理论目前收益(元)', '理论目前收益率(%)'],
cellStyle=cellsytle_jscode,
)
gb0.configure_side_bar()
gb0.configure_grid_options(domLayout='normal')
gridOptions = gb0.build()
grid_track = AgGrid(
data_track,
gridOptions=gridOptions,
allow_unsafe_jscode=True,
enable_enterprise_modules=True,
)
st.subheader("理论收益分析")
# Plot
x = data_track.sort_values(
by='理论目前收益率(%)',
)['名称'].tolist()
y1 = data_track.sort_values(
by='理论目前收益率(%)',
)['理论目前收益率(%)'].tolist()
y2 = data_track.sort_values(
by='理论目前收益率(%)',
)['理论目前收益(元)'].tolist()
fig0 = (
Bar(init_opts=opts.InitOpts(theme=ThemeType.MACARONS))
.add_xaxis(x)
.add_yaxis("理论目前收益率(%)", y1)
.add_yaxis("理论目前收益(元)", y2)
.reversal_axis()
.set_global_opts(
# 设置操作图表缩放功能,orient="vertical" 为Y轴 滑动
datazoom_opts=[
opts.DataZoomOpts(),
opts.DataZoomOpts(type_="inside", range_start=0, range_end=100),
opts.DataZoomOpts(
orient="vertical",
range_start=0,
range_end=100,
),
],
)
# .render("bar_datazoom_both.html")
)
streamlit_echarts.st_pyecharts(fig0, height="900px", key="fig0")
else:
st.caption("暂无已购饰品")
# 观望列表
st.subheader("观望列表")
observe = [xx for xx in goods if xx.cost == 0]
if len(observe) > 0:
data_observe = pd.DataFrame([xx() for xx in observe])
del data_observe['Cost']
data_observe['Status'] = data_observe['Status'].map({0: '观望中'})
data_observe.columns = [
'Buff id',
'有品 id',
'名称',
'Buff 价格(元)',
'有品价格(元)',
'Steam 价格(元)',
'状态',
'有品在售',
'有品在租',
'短租价格(元)',
'长租价格(元)',
'押金',
'租售比',
'租金比例(%)',
'押金比例(%)',
'年化短租比例(%)',
'年化长租比例(%)',
'套现比例(%)',
'buff和有品价格比例',
]
data_observe = data_observe.round(4)
del data_observe['Buff id']
del data_observe['有品 id']
gb1 = GridOptionsBuilder.from_dataframe(data_observe)
gb1.configure_columns(["Buff id", "有品 id", "名称"], pinned=True)
gb1.configure_side_bar()
gb1.configure_grid_options(domLayout='normal')
gridOptions = gb1.build()
grid_observe = AgGrid(
data_observe,
gridOptions=gridOptions,
allow_unsafe_jscode=True,
enable_enterprise_modules=True,
)
else:
st.caption("暂无观望饰品")
if __name__ == "__main__":
st.set_page_config(
"CSGO 饰品投资追踪",
"💰",
layout="wide",
)
USER_TOKEN="Bearer xxx"
main()