i think it is working
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,12 +1,12 @@
|
||||
from base_agent import BaseAgent
|
||||
from .base_agent import BaseAgent
|
||||
class AutoProductionAgent(BaseAgent):
|
||||
"""
|
||||
Automaticaly produces commodity if in business inventory
|
||||
"""
|
||||
|
||||
|
||||
def __init__(self,business) -> None:
|
||||
super().__init__()
|
||||
def __init__(self,sim,business) -> None:
|
||||
super().__init__(sim)
|
||||
self.business=business
|
||||
self.prod=business.production
|
||||
|
||||
@@ -14,7 +14,7 @@ class AutoProductionAgent(BaseAgent):
|
||||
|
||||
def can_produce(self):
|
||||
# If can produce item
|
||||
for k,cost in self.prod.items():
|
||||
for k,cost in self.prod["craft"].items():
|
||||
if cost > self.business.inventory[k]:
|
||||
return False
|
||||
return True
|
||||
@@ -23,7 +23,7 @@ class AutoProductionAgent(BaseAgent):
|
||||
if not self.can_produce():
|
||||
return
|
||||
# remove cost from inventory
|
||||
for k,cost in self.prod.items():
|
||||
for k,cost in self.prod["craft"].items():
|
||||
self.business.inventory[k]-=cost
|
||||
# add commodity
|
||||
self.business.inventory[self.prod['name']]+=self.prod["amount"]
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
class BaseAgent():
|
||||
def tick():
|
||||
import uuid
|
||||
from abc import ABC
|
||||
class BaseAgent(ABC):
|
||||
_bal="balance"
|
||||
def __init__(self,simulation) -> None:
|
||||
"""
|
||||
Base class of an agent.
|
||||
Simulation: to register tick method at.
|
||||
"""
|
||||
self.id=uuid.uuid4()
|
||||
self.simulation=simulation
|
||||
simulation.register_tick(self.id,self.tick)
|
||||
pass
|
||||
def tick(self):
|
||||
"""
|
||||
Tick method for simulation to call to execute selected action
|
||||
"""
|
||||
@@ -0,0 +1,98 @@
|
||||
from .base_agent import BaseAgent
|
||||
from ..exchange import Exchange
|
||||
from lightmatchingengine.lightmatchingengine import Order,Side
|
||||
from abc import ABC
|
||||
class Base_Aquire_Agent(BaseAgent,ABC):
|
||||
|
||||
def __init__(self,simulation,business,resource,exchanges: list) -> None:
|
||||
"""
|
||||
Agent for aquire of resources.
|
||||
business: Business to aquire resources for.
|
||||
resource: Resource to aquire.
|
||||
exchanges: list of exchanges to aquire resources from
|
||||
"""
|
||||
self.business=business
|
||||
self.resource=resource
|
||||
self.exchanges=exchanges
|
||||
self.orders={i: {} for i in range(len(self.exchanges))}
|
||||
self.target=0
|
||||
self.max_price=-1
|
||||
super().__init__(simulation)
|
||||
|
||||
def set_target(self,target: int):
|
||||
"""
|
||||
Sets the amount of resources the agent should aquire.
|
||||
Checks against current inventory of business.
|
||||
"""
|
||||
self.target=target
|
||||
|
||||
def target_error(self):
|
||||
"""
|
||||
Returns the difference between target and current business inventory.
|
||||
If err >0 the business is in surplus
|
||||
If err < 0 then agent needs to aquire resources
|
||||
"""
|
||||
err=self.business.inventory[self.resource]-self.target
|
||||
return err
|
||||
|
||||
def set_price_max(self,price: int):
|
||||
"""
|
||||
Sets the min price the agent should distribute the resources for.
|
||||
Set to -1 to disable minimum
|
||||
"""
|
||||
self.max_price=price
|
||||
|
||||
def order_resource(self,price_per, amount,cx_id):
|
||||
"""
|
||||
Aquire resource from selected exchange.
|
||||
Return order if order has been placed else None.
|
||||
"""
|
||||
# Get exchange
|
||||
cx=self.exchanges[cx_id]
|
||||
# Calculate order price
|
||||
total_price=price_per*amount
|
||||
|
||||
if not self.business.balance>=total_price:
|
||||
return None # we dont have enough balance
|
||||
|
||||
self.business.balance-=total_price
|
||||
cx.add_to_account(self.id,"balance",total_price) # prepaid charge account for cx
|
||||
order=cx.submit_order(self.id,self.resource,amount,price_per,Side.BUY)
|
||||
if order==None: # Order failed
|
||||
return False
|
||||
self.orders[cx_id][order.order_id]=order
|
||||
|
||||
return order
|
||||
|
||||
def collect_balance_from_cxs(self):
|
||||
"""
|
||||
Collects resouces from account inventory in cxs.
|
||||
"""
|
||||
for cx in self.exchanges:
|
||||
amount=cx.get_account_resource_amount(self.id,"balance")
|
||||
cx.remove_from_account(self.id,"balance",amount)
|
||||
self.business.balance+=amount
|
||||
|
||||
def collect_resource_from_cxs(self,resource):
|
||||
"""
|
||||
Collects resouces from account inventory in cxs.
|
||||
"""
|
||||
for cx in self.exchanges:
|
||||
amount=cx.get_account_resource_amount(self.id,resource)
|
||||
cx.remove_from_account(self.id,resource,amount)
|
||||
self.business.inventory[resource]+=amount
|
||||
|
||||
def update_trades(self) -> list:
|
||||
"""
|
||||
Returns a list of all trades performed by this agent
|
||||
"""
|
||||
trades=[]
|
||||
for cx_id in range(len(self.exchanges)):
|
||||
cx=self.exchanges[cx_id]
|
||||
orders=self.orders[cx_id]
|
||||
if len(orders)>0:
|
||||
for o in orders:
|
||||
trades.append(cx.order_trades_map[o.order_id])
|
||||
self.trades=trades
|
||||
return trades
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
from .base_agent import BaseAgent
|
||||
from ..exchange import Exchange
|
||||
from lightmatchingengine.lightmatchingengine import Order,Side
|
||||
from abc import ABC
|
||||
class Base_Distribution_Agent(BaseAgent,ABC):
|
||||
|
||||
def __init__(self,simulation,business,resource,exchanges: list) -> None:
|
||||
"""
|
||||
Agent for distribute of resources.
|
||||
business: Business to distribute resources for.
|
||||
resource: Resource to distribute.
|
||||
exchanges: list of exchanges to distribute resources to.
|
||||
"""
|
||||
self.business=business
|
||||
self.resource=resource
|
||||
self.exchanges=exchanges
|
||||
self.orders={i: {} for i in range(len(self.exchanges))}
|
||||
self.target=0
|
||||
self.min_price=-1
|
||||
super().__init__(simulation)
|
||||
|
||||
def set_target(self,target: int):
|
||||
"""
|
||||
Sets the amount of resources the agent should keep in business inventory
|
||||
"""
|
||||
self.target=target
|
||||
|
||||
|
||||
def set_price_min(self,price: int):
|
||||
"""
|
||||
Sets the min price the agent should distribute the resources for.
|
||||
Set to -1 to disable minimum.
|
||||
"""
|
||||
self.min_price=price
|
||||
|
||||
def target_error(self):
|
||||
"""
|
||||
Returns the difference between target and current business inventory.
|
||||
If err >0 the business is in surplus
|
||||
If err < 0 then agent needs to distribute resources
|
||||
"""
|
||||
err=self.business.inventory[self.resource]-self.target
|
||||
return err
|
||||
|
||||
|
||||
def distribute_resource(self,price_per,amount,cx_id):
|
||||
"""
|
||||
distribute resource from selected exchange.
|
||||
Return true if order has been placed.
|
||||
"""
|
||||
# Get exchange
|
||||
cx=self.exchanges[cx_id]
|
||||
|
||||
|
||||
if not self.business.inventory[self.resource]>=amount:
|
||||
return False # we dont have enough balance
|
||||
|
||||
self.business.inventory[self.resource]-=amount
|
||||
cx.add_to_account(self.id,self.resource,amount) # prepaid charge account for cx
|
||||
order=cx.submit_order(self.id,self.resource,amount,price_per,Side.SELL)
|
||||
if order==None: # Order failed
|
||||
return False
|
||||
self.orders[cx_id][order.order_id]=order
|
||||
return order
|
||||
|
||||
def collect_balance_from_cxs(self):
|
||||
"""
|
||||
Collects resouces from account inventory in cxs.
|
||||
"""
|
||||
for cx in self.exchanges:
|
||||
amount=cx.get_account_resource_amount(self.id,"balance")
|
||||
cx.remove_from_account(self.id,"balance",amount)
|
||||
self.business.balance+=amount
|
||||
|
||||
def collect_resource_from_cxs(self,resource):
|
||||
"""
|
||||
Collects resouces from account inventory in cxs.
|
||||
"""
|
||||
for cx in self.exchanges:
|
||||
amount=cx.get_account_resource_amount(self.id,resource)
|
||||
cx.remove_from_account(self.id,resource,amount)
|
||||
self.business.inventory[resource]+=amount
|
||||
|
||||
def update_trades(self) -> list:
|
||||
"""
|
||||
Returns a list of all trades performed by this agent
|
||||
"""
|
||||
trades=[]
|
||||
for cx_id in range(len(self.exchanges)):
|
||||
cx=self.exchanges[cx_id]
|
||||
orders=self.orders[cx_id]
|
||||
if len(orders)>0:
|
||||
for o in orders:
|
||||
trades.append(cx.order_trades_map[o.order_id])
|
||||
self.trades=trades
|
||||
return trades
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
from .base_aquire_agent import Base_Aquire_Agent
|
||||
|
||||
class Price_Believe_Aquire_Agent(Base_Aquire_Agent):
|
||||
"""
|
||||
Aquire agent with internal price believe system.
|
||||
"""
|
||||
def __init__(self, simulation, business, resource, exchanges: list, lr, max_price_adj_rate) -> None:
|
||||
super().__init__(simulation, business, resource, exchanges)
|
||||
self.lr=lr
|
||||
self.max_price_adj_rate=max_price_adj_rate
|
||||
self.price_believe=-1
|
||||
self.open_orders={i: [] for i in range(len(self.exchanges))}
|
||||
self.open_qty=0
|
||||
|
||||
def tick(self):
|
||||
if self.price_believe==-1:
|
||||
self.price_believe=self.max_price
|
||||
|
||||
order_error=self.open_qty+self.target_error()
|
||||
if order_error<0:
|
||||
# aquire based on current price belive
|
||||
order=self.order_resource(self.price_believe,order_error*-1,0)
|
||||
if not order==None:
|
||||
self.register_order(0,order)
|
||||
else:
|
||||
# order failed .. we need to adjust our price believe
|
||||
self.update_believe(-1)
|
||||
self.tick_open_orders()
|
||||
|
||||
|
||||
|
||||
def register_order(self,cx_id,order):
|
||||
self.open_orders[cx_id].append({
|
||||
'id': order.order_id,
|
||||
'lifetime': self.max_price_adj_rate,
|
||||
})
|
||||
|
||||
def tick_open_orders(self) -> int:
|
||||
"""
|
||||
Removes 1 from pending orders timeout timer and cancels timeed out orders.
|
||||
Returns ids of orders that timeed out.
|
||||
"""
|
||||
self.open_qty=0
|
||||
for cx_id in range(len(self.exchanges)):
|
||||
cx=self.exchanges[cx_id]
|
||||
cx_orders=self.open_orders[cx_id]
|
||||
for i in cx_orders:
|
||||
# Check for each order if it is fullfiled or if it is timed
|
||||
o=self.orders[cx_id][i["id"]]
|
||||
if o.leaves_qty==0:
|
||||
#order is done
|
||||
self.open_orders[cx_id].remove(i) # remove order from open
|
||||
self.update_believe(-1) # update price believe
|
||||
self.collect_balance_from_cxs()
|
||||
self.collect_resource_from_cxs(self.resource)
|
||||
continue
|
||||
if i["lifetime"]>0:
|
||||
self.open_qty+=o.leaves_qty
|
||||
i["lifetime"]-=1 # subtract lifetime
|
||||
else:
|
||||
# timeout
|
||||
cx.cancel_order(i["id"])
|
||||
self.collect_balance_from_cxs()
|
||||
self.collect_resource_from_cxs()
|
||||
self.update_believe(1)
|
||||
self.open_orders[cx_id].remove(i)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def update_believe(self,modifier):
|
||||
"""
|
||||
Updates the believe based on the modifier.
|
||||
If positive will add lr to believe
|
||||
If negative will sub lr to believe
|
||||
"""
|
||||
self.price_believe+=modifier*self.lr
|
||||
@@ -0,0 +1,75 @@
|
||||
from .base_distribution_agent import Base_Distribution_Agent
|
||||
|
||||
class Price_Believe_Distribiute_Agent(Base_Distribution_Agent):
|
||||
"""
|
||||
Aquire agent with internal price believe system.
|
||||
"""
|
||||
def __init__(self, simulation, business, resource, exchanges: list, lr, max_price_adj_rate) -> None:
|
||||
super().__init__(simulation, business, resource, exchanges)
|
||||
self.lr=lr
|
||||
self.max_price_adj_rate=max_price_adj_rate
|
||||
self.price_believe=-1
|
||||
self.open_orders={i: [] for i in range(len(self.exchanges))}
|
||||
self.open_qty=0
|
||||
|
||||
def tick(self):
|
||||
if self.price_believe==-1:
|
||||
self.price_believe=self.min_price
|
||||
|
||||
order_error=self.target_error()
|
||||
if order_error>0:
|
||||
# aquire based on current price belive
|
||||
order=self.distribute_resource(self.price_believe,order_error,0)
|
||||
self.register_order(0,order)
|
||||
self.tick_open_orders()
|
||||
|
||||
|
||||
|
||||
def register_order(self,cx_id,order):
|
||||
self.open_orders[cx_id].append({
|
||||
'id': order.order_id,
|
||||
'lifetime': self.max_price_adj_rate,
|
||||
})
|
||||
|
||||
def tick_open_orders(self) -> int:
|
||||
"""
|
||||
Removes 1 from pending orders timeout timer and cancels timeed out orders.
|
||||
Returns ids of orders that timeed out.
|
||||
"""
|
||||
self.open_qty=0
|
||||
for cx_id in range(len(self.exchanges)):
|
||||
cx=self.exchanges[cx_id]
|
||||
cx_orders=self.open_orders[cx_id]
|
||||
for i in cx_orders:
|
||||
# Check for each order if it is fullfiled or if it is timed
|
||||
o=self.orders[cx_id][i["id"]]
|
||||
if o.leaves_qty==0:
|
||||
#order is done
|
||||
self.open_orders[cx_id].remove(i) # remove order from open
|
||||
self.update_believe(1) # update price believe
|
||||
self.collect_balance_from_cxs()
|
||||
self.collect_resource_from_cxs(self.resource)
|
||||
continue
|
||||
if i["lifetime"]>0:
|
||||
self.open_qty+=o.leaves_qty
|
||||
i["lifetime"]-=1 # subtract lifetime
|
||||
else:
|
||||
# timeout
|
||||
cx.cancel_order(i["id"])
|
||||
self.collect_balance_from_cxs()
|
||||
self.collect_resource_from_cxs(self.resource)
|
||||
self.update_believe(-1)
|
||||
self.open_orders[cx_id].remove(i)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def update_believe(self,modifier):
|
||||
"""
|
||||
Updates the believe based on the modifier.
|
||||
If positive will add lr to believe
|
||||
If negative will sub lr to believe
|
||||
"""
|
||||
self.price_believe+=modifier*self.lr
|
||||
@@ -0,0 +1,21 @@
|
||||
from .base_business import Business
|
||||
from ..agents.price_believe_aquire import Price_Believe_Aquire_Agent
|
||||
from ..agents.price_believe_distribute import Price_Believe_Distribiute_Agent
|
||||
from ..agents.autoproduction import AutoProductionAgent
|
||||
class Price_Believe_Business(Business):
|
||||
def __init__(self, id, production, balance,exchange,simulation) -> None:
|
||||
super().__init__(id, production, balance)
|
||||
|
||||
self.distribute=Price_Believe_Distribiute_Agent(simulation,self,production["name"],exchange,1,50)
|
||||
self.craft=AutoProductionAgent(simulation,self)
|
||||
self.aquire={}
|
||||
for k,v in production["craft"].items():
|
||||
a=Price_Believe_Aquire_Agent(simulation,self,k,exchange,1,50)
|
||||
a.set_target(v*10)
|
||||
a.set_price_max(10)
|
||||
self.aquire[k]=a
|
||||
self.distribute.set_price_min(10)
|
||||
self.distribute.set_target(0)
|
||||
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -1,4 +1,5 @@
|
||||
class Business():
|
||||
from abc import ABC
|
||||
class Business(ABC):
|
||||
def __init__(self,id,production,balance) -> None:
|
||||
"""production (dict): {
|
||||
name: 'Gem',
|
||||
@@ -15,9 +16,10 @@ class Business():
|
||||
self.balance=balance
|
||||
#Setup Inventory
|
||||
self.inventory={production["name"]: 0}
|
||||
for k in production["craft"].items():
|
||||
for k,v in production["craft"].items():
|
||||
self.inventory[k]=0
|
||||
|
||||
pass
|
||||
|
||||
def step_business_decisions(self):
|
||||
pass
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
commoditys=[
|
||||
{
|
||||
'name': 'Gem',
|
||||
'amount': 4,
|
||||
'craft': {
|
||||
'Raw_Gem': 4,
|
||||
'Tool_Gem': 0.2,
|
||||
}
|
||||
},
|
||||
{
|
||||
'name': 'Tool_Gem',
|
||||
'amount': 1,
|
||||
'craft': {
|
||||
'Stone': 1,
|
||||
'Wood': 1,
|
||||
}
|
||||
},
|
||||
{
|
||||
'name': 'Wood',
|
||||
'amount': 1,
|
||||
'craft': {
|
||||
'Raw_Wood': 1,
|
||||
}
|
||||
},
|
||||
{
|
||||
'name': 'Stone',
|
||||
'amount': 1,
|
||||
'craft': {
|
||||
'Raw_Stone': 1,
|
||||
}
|
||||
},
|
||||
]
|
||||
|
||||
+61
-12
@@ -1,5 +1,5 @@
|
||||
_bal="balance"
|
||||
from lightmatchingengine.lightmatchingengine import LightMatchingEngine,Side,Trade,Order
|
||||
from lightmatchingengine.lightmatchingengine import LightMatchingEngine,Side,Trade,Order,OrderBook
|
||||
class Exchange():
|
||||
"""
|
||||
Basic Commodity exchange.
|
||||
@@ -13,7 +13,7 @@ class Exchange():
|
||||
self.order_account_map={}
|
||||
self.orders={}
|
||||
self.executed_trades=[]
|
||||
self.order_total_price={}
|
||||
self.order_trades_map={}
|
||||
self.market_rate={}
|
||||
self.best_ask={}
|
||||
self.best_bid={}
|
||||
@@ -116,12 +116,48 @@ class Exchange():
|
||||
# no sufficient resources
|
||||
return None
|
||||
# create order and execude any trades
|
||||
order,trades=self.lme.add_order(resource,price,amount,side)
|
||||
|
||||
order,trades=self.lme.add_order(resource,price,amount,side)
|
||||
|
||||
self.orders[order.order_id]=order
|
||||
self.order_account_map[order.order_id]=account_id
|
||||
self._execute_trades(trades)
|
||||
self.calculate_best_price(resource)
|
||||
return order
|
||||
|
||||
def cancel_order(self,order_id):
|
||||
"""
|
||||
Cancel open order.
|
||||
Returns True if order has been canceled.
|
||||
"""
|
||||
if not order_id in self.orders:
|
||||
return False
|
||||
order=self.orders[order_id]
|
||||
if self._is_order_complete(order):
|
||||
return False
|
||||
order=self.lme.cancel_order(order_id,order.instmt)
|
||||
|
||||
if order_id in self.order_trades_map: # has order any kind of trades
|
||||
trades=self.order_trades_map[order_id]
|
||||
# return assets from escrow to account
|
||||
if order.side==Side.BUY:
|
||||
total_payed=0
|
||||
total_submitted=order.price*order.qty
|
||||
for trade in trades:
|
||||
total_payed+=trade.trade_price*trade.trade_qty
|
||||
total_diff=total_submitted-total_payed
|
||||
self._move_to_account(self.order_account_map[order_id],_bal,total_diff)
|
||||
else:
|
||||
qty_traded=0
|
||||
qty_submitted=order.qty
|
||||
for trade in trades:
|
||||
qty_traded+=trade.trade_qty
|
||||
qty_diff=qty_submitted-qty_traded
|
||||
self._move_to_account(self.order_account_map[order_id],order.instmt,qty_diff)
|
||||
return True
|
||||
|
||||
|
||||
|
||||
def _execute_trades(self, trades):
|
||||
for i in trades:
|
||||
self._execute_trade(i)
|
||||
@@ -136,11 +172,13 @@ class Exchange():
|
||||
full_price=trade.trade_price*trade.trade_qty
|
||||
exprected_price=self.orders[trade.order_id].price*trade.trade_qty
|
||||
price_diff=exprected_price-full_price
|
||||
# add to order total
|
||||
if trade.order_id not in self.order_total_price:
|
||||
self.order_total_price[trade.order_id]=0
|
||||
|
||||
self.order_total_price[trade.order_id]+=full_price
|
||||
# save trade to order trade map
|
||||
if trade.order_id not in self.order_trades_map:
|
||||
self.order_trades_map[trade.order_id]=[]
|
||||
|
||||
self.order_trades_map[trade.order_id].append(trade)
|
||||
|
||||
|
||||
# init escrow if needet
|
||||
if trade.instmt not in self.escrow[account_id]:
|
||||
@@ -160,13 +198,24 @@ class Exchange():
|
||||
# move new bal into account
|
||||
self._move_to_account(account_id,_bal,full_price)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
self.executed_trades.append(trade)
|
||||
#update market rate
|
||||
self.market_rate[trade.instmt]=trade.trade_price
|
||||
|
||||
|
||||
def calculate_best_price(self, resource):
|
||||
order_book = self.lme.order_books.setdefault(resource, OrderBook())
|
||||
best_bid = max(order_book.bids.keys()) if len(order_book.bids) > 0 else None
|
||||
best_ask = max(order_book.asks.keys()) if len(order_book.asks) > 0 else None
|
||||
|
||||
|
||||
self.best_ask[resource]=best_ask
|
||||
self.best_bid[resource]=best_bid
|
||||
|
||||
def get_account_resource_amount(self,account_id,resource):
|
||||
"""
|
||||
Returns the amount of selected resource in account
|
||||
"""
|
||||
if account_id in self.account:
|
||||
if resource in self.account[account_id]:
|
||||
return self.account[account_id][resource]
|
||||
return 0
|
||||
@@ -0,0 +1,27 @@
|
||||
import random
|
||||
class Simulation():
|
||||
"""
|
||||
Class for controlling the different calculation steps of the Simulation.
|
||||
"""
|
||||
def __init__(self) -> None:
|
||||
self.tick_funcs={}
|
||||
pass
|
||||
|
||||
def seed(self,a):
|
||||
"""
|
||||
Sets the random seed
|
||||
"""
|
||||
random.seed(a)
|
||||
def register_tick(self,id,tickfunc):
|
||||
|
||||
self.tick_funcs[id]=tickfunc
|
||||
|
||||
def tick_random_order(self):
|
||||
"""
|
||||
Ticks all agents in a Random Order
|
||||
"""
|
||||
keys=list(self.tick_funcs.keys())
|
||||
random.shuffle(keys)
|
||||
for k in keys:
|
||||
fun=self.tick_funcs[k]
|
||||
fun()
|
||||
Reference in New Issue
Block a user