CCF-BDCI基于买方意向的货物撮合交易-方案分享

感谢starry老哥的投稿,下面就来介绍本次比赛的背景任务和他优秀的解决思路吧(文末附有136分的完整代码链接哦)!!!

赛题链接

链接

赛题背景

期货市场上的货物卖方和货物的买方期望通过期货市场进行货物买卖,达到买卖双方钱货交换的目的(买方从卖方获取货物并向卖方支付对应货款)。

赛题任务

卖方客户已公布需要卖出的货物具体信息,如表格1所示。
表格 1 卖方已公布货物信息

图片

买方客户群体确定,买方客户根据自身需求,并根据卖方客户已公布的货物信息,申报所需购买货物的意向(如表格2所示),当然客户也可不申报意向(购买货物种类、数量确定,只是不指定希望购买货物的属性),由系统进行分配。

 

表格 2 买方客户购买货物意向

图片

任务:通过系统按照买方意向将卖方客户所公布的货物分配给买方客户,要求买方客户的满意率最高,其中买方客户在获取按其意向要求货物的同时,拿到的货物尽量在一个仓库中,则其满意率最高。总体满意率按客户购买货物数量加权计算。

约束条件:具体分配时,优先考虑客户的第一意向,当存在客户的第一意向未满足或者部分未满足时,再考虑客户的第二意向,依次类推。
第一意向在同意向维度、同意向值的情况下,优先为“平均持仓时间”(在买方客户购买意向数据中给出)长的买方客户分配货物。
在按照前述条件下分配货物时,优先分配入库时间长的货物。

评测标准

一、评分原则

在正确反应题目要求和规则的前提下,区分不同参赛队伍算法的相对优劣。


二、计算方法


算法评分包含客户评分和算法整体评分,总分根据以上两项评分进行计算,具体如下

1.在单个品种上,每个客户满意率评分的维度分为意向分和记录分2项,所占权重分别为0.6和0.4,计算规则如下
1)意向分(Hope_score)
根据每个意向上分配的货物数量计算加权得分,具体计算公式如下

图片

j是货物编号,货物编号j的属性满足第k意向(k可以是多个值),POSij指货物编号j的分配货物数量,POSi为客户i 在某品种上购买货物的总量,SCOREk指货物编号j在第k意向上的得分(各品种在每个意向上的得分见下表)。

2)记录分(Diary_score)
客户分到仓库的记录数越少越好,该值设置的目标为梯度得分,直至扣分。具体计算如下:

图片

3.总分计算
单个品种下score公式为:
图片
其中意向分和记录分按客户购买货物数量计算加权得分(大客户分数所占权重更大),此公式中i指客户。
参赛者的总分为各品种下得分加和:
图片
此公式中i指品种。

解决方案

给每一个buyer分配资源,分配的规则是意向优先,尽可能让该buyer满足更多的意向

 1   def search_for_one_buyer(self,var,yixiangs,buyer_id,buyer_index):
 2        """
 3        :param var:  SR CF
 5        :param yixiangs:     该buyer的意向
 6        :param buyer_id:     该buyer的id
 7        :param buyer_index: 该buyer在self.buyer的index
 8        :return:             不返回值,更新剩余资源和Buyer信息
 9        """
10        buyer_tem=self.tem_buyer.copy()
11        tem_seller=self.tem_seller.copy()
12        number = buyer_tem.loc[buyer_index,'购买货物数量']#.values[0]
13        satisfy_yixiang = [str(self.tem_yixiang_index+1)]
14        for i, (yixiang, yixiang_value) in enumerate(yixiangs):
15            if pd.isna(yixiang):
16                continue
17            if yixiang == '仓库' or yixiang == '年度':
18                yixiang_value = int(yixiang_value)
19            if sum(tem_seller[tem_seller[yixiang] == yixiang_value]['货物数量(张)']) > 0: ##尽可能的取出可以满足多个意向的
20                tem_seller = tem_seller[tem_seller[yixiang] == yixiang_value]
21                satisfy_yixiang.append(str(i + self.tem_yixiang_index+2))
22        tem_seller = tem_seller.sort_values(by=['货物数量(张)'], ascending=True)
23        for i in tem_seller.index:
24            good_num = tem_seller.loc[i]['货物数量(张)']
25            if good_num<=0:
26                continue
27            seller_id=tem_seller.loc[i]['卖方客户']
28            good_id=tem_seller.loc[i]['货物编号']
29            ware_id=tem_seller.loc[i]['仓库']
30
31            # print(good_num)
32            if good_num >= number:
33                tem_seller.loc[i,'货物数量(张)'] = good_num - number
34                self.tem_seller.loc[i,'货物数量(张)'] = good_num - number  ##真实的记录下来
35                self.seller.loc[i,'货物数量(张)']=good_num - number
36                self.result.append([buyer_id, seller_id, var, good_id, ware_id, number, '-'.join(satisfy_yixiang)])
37                number = 0
38                break
39            else:
40                self.result.append([buyer_id, seller_id, var, good_id, ware_id, good_num, '-'.join(satisfy_yixiang)])
41                number = number - good_num
42                tem_seller.loc[i,'货物数量(张)'] = 0  #tem_seller.loc[i]['货物数量(张)'] -good_num
43                self.tem_seller.loc[i,'货物数量(张)'] =0  # self.tem_seller.loc[i]['货物数量(张)']-good_num
44                self.seller.loc[i, '货物数量(张)']=0
45                if number==0:
46                    break
47        self.tem_buyer.loc[buyer_index,'购买货物数量']=number
48        self.buyer.loc[buyer_index,'购买货物数量']=number
49        if number > 0 and sum(self.tem_seller['货物数量(张)']) > 0: ##如果满足最基础意向的还有剩下的,那么就继续  这两个哪怕有一个等一0,那么都不需要继续了。
50            self.search_for_one_buyer(var,yixiangs,buyer_id,buyer_index)

 

根据意向和购买数量对Buyer设定权重

 1    def getorder(self,buyer_tm,var,col1):
 2       
 3        if var == "SR":
 4            weight = [40, 30, 20, 10]
 5            for i in range(self.tem_yixiang_index, 4):
 6                buyer_tm.loc[buyer_tm.index, '权值系数'] = buyer_tm.loc[buyer_tm.index, '权值系数'] + pd.notna(buyer_tm.loc[buyer_tm.index, col1[i]]).astype(int) * weight[i]
 7        else:
 8            weight = [33, 27, 20, 13, 7]
 9            for i in range(self.tem_yixiang_index, 5):
10                buyer_tm.loc[buyer_tm.index, '权值系数'] = buyer_tm.loc[buyer_tm.index, '权值系数'] + pd.notna(
11                    buyer_tm.loc[buyer_tm.index, col1[i]]).astype(int) * weight[i]
12        buyer_tm.loc[buyer_tm.index, '权重'] = buyer_tm.loc[buyer_tm.index, '权值系数'] * buyer_tm.loc[buyer_tm.index, '购买货物数量']
13        buyer_tm=buyer_tm.sort_values(by=['权重'],ascending=True)
14        return buyer_tm

 

选择相同意向的buyer进行优先级分类

 1    def search_sameyixiang(self,emotions_and_good):
 2        """
 3        :param emotions_and_good:
 5        :return:
 6        """
 7        for dis, var, sum1, _ in emotions_and_good:
 8            print(dis,var,sum1)
 9            if sum1<=0:
10                continue
11            thefirst = self.buyer[self.buyer[self.yixiang2value[self.tem_yixiang]] == dis][self.tem_yixiang].iloc[0]
12            self.tem_buyer = self.buyer[(self.buyer[self.yixiang2value[self.tem_yixiang]] == dis) & (self.buyer['品种'] == var)]
13            if thefirst=='年度' or thefirst=='仓库':
14                dis=int(dis)
15            self.tem_seller=self.seller[(self.seller[thefirst]==dis) & (self.seller['品种']==var)]  ##取出这个品种下这个意向的所有seller
16            print('第几意向:', str(self.tem_yixiang_index + 1), '该意向下Buyer数量:', len(self.tem_buyer), "seller数量:",
17                  len(self.tem_seller), '买方的总数量:', sum(self.tem_buyer['购买货物数量']), '卖方总数量:',
18                  sum(self.tem_seller['货物数量(张)']))
19
20            if sum(self.tem_seller['货物数量(张)'])==0:  #如果满足这个意向的货物没有了,那么就不需要了
21                continue
22            self.tem_buyer.loc[self.tem_buyer.index, '权值系数'] = 0
23
24            if self.tem_yixiang_index==0 and sum(self.tem_seller['货物数量(张)'])<sum(self.tem_buyer['购买货物数量']): ##只要在是第一意向并且卖的货物少于买的货物的时候才会这
25                self.tem_buyer=self.tem_buyer.sort_values(by=['平均持仓时间','购买货物数量'],ascending=False) ##根据平均持仓时间倒序,持仓时间越长的越优先挑选货物
26                ##我只要把持有时间长的都弄为第一意向就可以了,别的人就不管他了。
27                sum_seller= sum(self.tem_seller['货物数量(张)']) ##总共有这么多
28                for ii,ij in enumerate(self.tem_buyer.index):
29                    if sum_seller>0:
30                        sum_seller=sum_seller-self.tem_buyer.loc[ij,'购买货物数量']
31                    else:
32                        ##把前面哪些人供应好了就可以了,后面的可以不管
33                        tem_buyer1=self.tem_buyer[:ii]
34                        if sum(tem_buyer1['购买货物数量'])>sum(self.tem_seller['货物数量(张)']): ##这么多的已经足够消化了
35                            print('1111')
36                        tem_buyer2=self.tem_buyer[ii:ii+1]
37                        tem_buyer3=self.tem_buyer[ii+1:]
38                        break
39                tem_buyer1=self.getorder(tem_buyer1,var,list(self.yixiang2value.keys()).copy())
40                tem_buyer3 = self.getorder(tem_buyer3, var, list(self.yixiang2value.keys()).copy())
41                self.tem_buyer=pd.concat([tem_buyer1,tem_buyer2,tem_buyer3])
42            else:##否则按照购买数量进行排序,购买数量越多的就越优先
43                self.tem_buyer=self.getorder(self.tem_buyer,var,list(self.yixiang2value.keys()).copy())
44
45            buyer_tem_len = len(self.tem_buyer)
46
47            for i,buyer_index in enumerate(self.tem_buyer.index):
48                buyer_id=self.tem_buyer.loc[buyer_index,'买方客户']
49                if i==buyer_tem_len-1:
50                    print("第几个买家:",i+1,"多少买家:",buyer_tem_len ,"买家id:",buyer_id,"时间:",datetime.datetime.now(),sum(self.buyer['购买货物数量'])==sum(self.seller['货物数量(张)'])) #
51                if sum(self.tem_seller['货物数量(张)']) == 0:                        ##如果满足意向的货物没有了,那么后面的都不需要再满足了。
52                    break
53                yixiangs=[]
54                for i in range(self.tem_yixiang_index+1,len(self.yixiang2value)):
55                    yixiang=list(self.yixiang2value.keys())[i]
56                    yixiang_value=self.yixiang2value[yixiang]
57                    yixiangs.append(yixiang)
58                    yixiangs.append(yixiang_value)
59                tt = self.tem_buyer[self.tem_buyer.index== buyer_index][yixiangs]
60                tt = tt.values[:1].tolist()[0]
61                yixiangs=[(tt[i],tt[i+1]) for i in range(0,len(tt),2)]
62                self.search_for_one_buyer(var,yixiangs,buyer_id,buyer_index)
63                self.tem_seller = self.tem_seller [self.tem_seller ['货物数量(张)'] > 0]
64                if i==buyer_tem_len-1:
65                    print("该意向下还剩下多少卖家:",len(self.tem_seller))
66            self.seller=self.seller[self.seller['货物数量(张)']>0]
67            self.buyer=self.buyer[self.buyer['购买货物数量']>0]
68            print("目前剩下多少买家:",len(self.buyer))
69            print("目前剩下多少卖家:",len(self.seller))

与search_for_one_buyer相似,只是不需要在乎意向。

 1    def search_by_noyixiang_onebuy(self,buyer_id,var,buyer_index):
 2        """
 3        :param buyer_id:
 5        :param var:
 6        :param buyer_index:
 7        :return:
 8        """
 9        buyer_tem = self.tem_buyer.copy()
10        tem_seller = self.tem_seller.copy()
11        satisfy_yixiang="0"
12        number = buyer_tem.loc[buyer_index, '购买货物数量']
13        tem_seller = tem_seller.sort_values(by=['货物数量(张)'], ascending=True)
14        for i in tem_seller.index:
15            seller_id=tem_seller.loc[i]['卖方客户']
16            good_id=tem_seller.loc[i]['货物编号']
17            ware_id=tem_seller.loc[i]['仓库']
18            good_num=tem_seller.loc[i]['货物数量(张)']
19            if good_num==0:
20                break
21            index = i
22            if good_num >= number:
23                tem_seller.loc[i,'货物数量(张)'] = good_num - number
24                self.tem_seller.loc[i,'货物数量(张)'] = good_num - number  ##真实的记录下来
25                self.seller.loc[i,'货物数量(张)']=good_num - number
26                self.result.append([buyer_id, seller_id, var, good_id, ware_id, number, satisfy_yixiang])
27                number = 0
28                break
29            else:
30                self.result.append([buyer_id, seller_id, var, good_id, ware_id, good_num, satisfy_yixiang])
31                number = number - good_num
32                tem_seller.loc[i,'货物数量(张)'] = 0  #tem_seller.loc[i]['货物数量(张)'] -good_num
33                self.tem_seller.loc[i,'货物数量(张)'] =0  # self.tem_seller.loc[i]['货物数量(张)']-good_num
34                self.seller.loc[i, '货物数量(张)']=0
35                if number==0:
36                    break
37        self.tem_buyer.loc[buyer_index, '购买货物数量'] = number
38        self.buyer.loc[buyer_index, '购买货物数量'] = number

 

与search_sameyixiang相似,只是不需要在乎意向

 1    def search_by_noyixiang(self):
 2        """
 3        :return:
 5        """
 6        for var in ["SR","CF"]:
 7            self.tem_seller=self.seller[self.seller['品种']==var]
 8            self.tem_buyer=self.buyer[self.buyer['品种']==var]
 9            self.tem_buyer=self.tem_buyer.sort_values(by=['购买货物数量'], ascending=True)
10            buyer_tem_len = len(self.tem_buyer)
11            print("该情况下买方用户的数量", buyer_tem_len)
12            for i, buyer_index in enumerate(self.tem_buyer.index):
13                buy_id=self.tem_buyer.loc[buyer_index,'买方客户']
14                if i==buyer_tem_len-1:
15                    print("第几个买家:", i + 1, "多少买家:", buyer_tem_len, "买家id:", buy_id, "时间:", datetime.datetime.now(),sum(self.buyer['购买货物数量'])==sum(self.seller['货物数量(张)']))
16                self.search_by_noyixiang_onebuy(buy_id,var,buyer_index)
17                self.tem_seller = self.tem_seller[self.tem_seller['货物数量(张)'] > 0]
18                if i==buyer_tem_len-1:
19                    print("该意向下还剩下多少卖家:", len(self.tem_seller))
20            self.seller=self.seller[self.seller['货物数量(张)']>0]
21            self.buyer=self.buyer[self.buyer['购买货物数量']>0]
22            print("目前剩下多少买家:",len(self.buyer))
23            print("目前剩下多少卖家:",len(self.seller))

 

从第一意向到第五意向搜索。

 1    def sort_by_xiyang(self):
 2        """
 3        :return:
 5        """
 6        if self.tem_yixiang_index<5:
 7            self.tem_yixiang = list(self.yixiang2value.keys())[self.tem_yixiang_index]
 8            tem_buyer=self.buyer[~pd.isna(self.buyer[self.tem_yixiang])]
 9            tem_yixiang_value=self.yixiang2value[self.tem_yixiang]
10            emotions = list(set(tem_buyer[tem_yixiang_value]))
11            emotions_and_good = []
12            for i in emotions:
13                tem1 = tem_buyer[(tem_buyer[tem_yixiang_value] == i) & (tem_buyer['品种'] == 'SR')]["购买货物数量"].sum()
14                tem2 = tem_buyer[(tem_buyer[tem_yixiang_value] == i) & (tem_buyer['品种'] == 'CF')]["购买货物数量"].sum()
15                emotions_and_good.append((i, "SR", tem1, tem1 * self.SR_value[self.tem_yixiang_index]))
16                emotions_and_good.append((i, "CF", tem2, tem2 * self.CF_value[self.tem_yixiang_index]))
17
18            emotions_and_good=[i for i in emotions_and_good if i[2]>0]
19            emotions_and_good = sorted(emotions_and_good, key=lambda x: x[3])#[::-1]
20            self.search_sameyixiang(emotions_and_good)

关注公众号ChallengeHub回复“ccf购买”获取136分的完整代码链接。加入ChallengeHub粉丝群,共同探讨,共同学习,共同进步!!!
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值