How do you model multiple arrival distributions? - python

Python:
I am simulating a call-centre with 2 types of incoming calls: Sales calls, and service calls.
These calls have different, independent distributions, which enter the same system.
I have function, arrivals which contains:
iat_sales = random.expovariate(1/3)
yield env.timeout(iat_sales)
I want to incorporate:
iat_service = random.triangular(0,0,6)
yield env.timeout(iat_service)
how can I yield each event simultaneously?
This is the solution I have come up with:
def arrival_list():
sales_time = 0 #sim time of sales arrival
service_time = 0 #sim time of service arrival
sales_list=[] #list of sequential sales arrivals [arrival time,'sales']
service_list=[] #list of sequential sales arrivals [arrival time,'service']
arrivals = [] #ordered list of arrivals (sales and service merged) [arrival time,arrival type,iat]
while sales_time < sim_end:
iat_sales = random.expovariate(sales_rate)
sales_time += iat_sales
sales=[sales_time,'sales']
sales_list.append(sales)
while service_time < sim_end:
iat_service = random.triangular(0,6,0) ####
service_time += iat_service
service=[service_time,'service']
service_list.append(service)
arrivals = sales_list + service_list
arrivals.sort()
arrivals[0].append(arrivals[0][0])
for i in range(len(arrivals)-1):
arrivals[i+1].append(arrivals[i+1][0]-arrivals[i][0])
return arrivals

As a reference, a simple implementation can be done like this, where a simulation is run indefinitely with 1 second intervals and calls are considered to arrive if their random values exceed some thresholds:
import random
import time
def generate_calls():
return random.expovariate(1/3), random.triangular(10, 20, 5)
def simulation(count, sales_acceptance, services_acceptance):
# run the simulation indefinitely
while True:
print('Time: {}'.format(count))
sales_call, services_call = generate_calls()
# calls arrive if the values exceed some thresholds
if sales_call > sales_acceptance:
print('Sales call arrived!')
if services_call > services_acceptance:
print('Services call arrived!')
time.sleep(1)
count += 1
simulation(1, 2, 13)

You can have three separate parallel processes.
1- One process for making Sales calls.
2- One process for making service calls.
3- One process for handling calls.
import simpy
import random
sim_end = 1000;
def generateSalesCall(env, call_pipe):
while env.now < sim_end:
# put call in the pipe
yield call_pipe.put("sales");
interval = random.expovariate(1/3);
yield env.timeout(interval);
def generateServiceCall(env, call_pipe):
while env.now < sim_end:
# put call in the pipe
yield call_pipe.put("service");
interval = random.triangular(0,6,0);
yield env.timeout(interval);
def handleCalls(env, call_pipe):
while(True):
call = yield call_pipe.get();
if call == "sales":
print(env.now, "sales call");
elif call == "service":
print(env.now, "service call");
env = simpy.Environment();
call_pipe = simpy.Store(env);
env.process(generateSalesCall(env, call_pipe));
env.process(generateServiceCall(env, call_pipe));
env.process(handleCalls(env, call_pipe));
env.run();

Related

How do I add a daterange to open trades during when backtesting with backtrader?

I am trying to backtest a strategy where trades are only opened during 8.30 to 16.00 using backtrader.
Using the below attempt my code is running but returning no trades so my clsoing balane is the same as the opening. If I remove this filter my code is running correctly and trades are opening and closing so it is definitely the issue. Can anyone please help?
I have tried adding the datetime column of the data to a data feed using the below code:
` def __init__(self):
# Keep a reference to the "close" line in the data[0] dataseries
self.dataclose = self.datas[0].close
self.datatime = mdates.num2date(self.datas[0].datetime)
self.datatsi = self.datas[0].tsi
self.datapsar = self.datas[0].psar
self.databbmiddle = self.datas[0].bbmiddle
self.datastlower = self.datas[0].stlower
self.datastupper = self.datas[0].stupper
# To keep track of pending orders
self.order = None`
I then used the following code to try filter by this date range:
# Check if we are in the market
if not self.position:
current_time = self.datatime[0].time()
if datetime.time(8, 30) < current_time < datetime.time(16, 0):
if self.datatsi < 0 and self.datastupper[0] > self.dataclose[0] and self.datastlower[1] < self.dataclose[1] and self.dataclose[0] < self.databbmiddle[0] and self.datapsar[0] > self.dataclose[0]:
self.log('SELL CREATE, %.2f' % self.dataclose[0])
# Keep track of the created order to avoid a 2nd order
os = self.sell_bracket(size=100,price=sp1, stopprice=sp2, limitprice=sp3)
self.orefs = [o.ref for o in os]
else:
o1 = self.buy(exectype=bt.Order.Limit,price=bp1,transmit=False)
print('{}: Oref {} / Buy at {}'.format(self.datetime.date(), o1.ref, bp1))
o2 = self.sell(exectype=bt.Order.Stop,price=bp2,parent=o1,transmit=False)
print('{}: Oref {} / Sell Stop at {}'.format(self.datetime.date(), o2.ref, bp2))
o3 = self.sell(exectype=bt.Order.Limit,price=bp3,parent=o1,transmit=True)
print('{}: Oref {} / Sell Limit at {}'.format(self.datetime.date(), o3.ref, bp3))
self.orefs = [o1.ref, o2.ref, o3.ref] # self.sell(size=100, exectype=bt.Order.Limit, price=self.data.close[0]+16, parent=self.order, parent_bracket=bt.Order.Market)

How can I add the following stochastic elements to my simulation of a multiple machine with buffer system?

I have gotten a few steps further along with my simulation and analysis of the machines that I want to simulate. As of now I am stuck and do not for the life of my know how to go further.
I have decided to add stochastic elements to my machine. Before (see code below) I had a Machine class that has produce, failure and repair process. My professor and I have decided that using stochasticity will be a better representation of reality. Therefore I have the following elements I would like to add and explanation of the process:
All Machines starts in state 1
Every second (tick) a processing speed is chosen from possibilities (% chance)
Immediately a time in state is chosen from distribution (or random.choice() for ease of use)
After the time in state has passed, a new state is chosen from possibilities (% chance)
The process repeats until simulation time is over.
There is no more machine failure and repair process as this is taken up in the different states.
I have an idea that I will need to make 3 functions, for determining state, speed and time in state only I have no idea how to do this.
The different parameters/possibilities per machine are as follows:
Machine 1
If in state 1:
Possible speeds (in ticks): { 3: 54%, 4: 24%, 5 : 22% }
Duration: Exponential distribution or random.choice()
Possible next state after duration: { 0: 41%, 2: 54%}
If in state 2:
Possible speeds (in ticks): { 2: 90%, 0: 10% }
Duration: Exponential distribution or random.choice()
Possible next state after duration: { 0: 60%, 1: 40%}
If in state 0:
Possible speeds (in ticks): { 0: 100% }
Duration: Exponential distribution or random.choice()
Possible next state after duration: { 1: 100% }
Machine 2
If in state 1:
Possible speeds (in ticks): { 5: 42%, 4: 34%, 7 : 24% }
Duration: Exponential distribution or random.choice()
Possible next state after duration: { 0: 70%, 2: 30%}
If in state 2:
Possible speeds (in ticks): { 1: 80%, 0: 20% }
Duration: Exponential distribution or random.choice()
Possible next state after duration: { 0: 90%, 1: 10%}
If in state 0:
Possible speeds (in ticks): { 0: 100% }
Duration: Exponential distribution or random.choice()
Possible next state after duration: { 1: 100% }
Any help will be much much appreciated!
I am adding the code of my simulation as I have it now. I do know that a few elements are obsolete due to now needing to fail and repair anymore but I am not sure how to change it up.
# Parameters for the Machines
# Machine 1
speed_1 = 0.1 # Avg. processing time of Machine 1 in minutes
# speed_1_stdev = 0.6 # St. dev. of processing time of Machine 1
MTTF_1 = 9 # Mean time to failure Machine 1
# fail_1 = 1/MTTF_1 # Parameter for exp. distribution
repair_1 = 5 # Time it takes to repair Machine 1
# Machine 2
speed_2 = 0.15 # Processing time of Machine 2 in minutes
# speed_2_stdev = 0.6 # St. dev. of processing time of Machine 2
MTTF_2 = 7 # Mean time to failure Machine 2
# fail_2 = 1/MTTF_2 # Parameter for exp. distribution
repair_2 = 3 # Time it takes to repair Machine 2
# Simulation time
time = 60 # Sim time in minutes
# Class setup for a Machine
class Machine(object):
"""
A machine produces units at a fixed processing speed,
takes units from a store before and puts units into a store after.
Machine has a *name*, a processing speed *speed*, a preceeding buffer *in_q*,
and a proceeding buffer *out_q*.
"""
def __init__(self, env, name, in_q, out_q, speed, mttf, repair):
self.env = env
self.name = name
self.in_q = in_q
self.out_q = out_q
self.speed = speed
self.mttf = mttf
self.repair = repair
self.broken = False
self.count = 0
# Start the producing process
self.process = env.process(self.produce())
# Start the failure process
env.process(self.fail_machine())
def produce(self):
"""
Produce parts as long as the simulation runs.
It is prone to breaking down and being repaired.
"""
while True:
pspeed = self.speed
try:
yield env.timeout(pspeed)
if len(self.out_q.items) >= self.out_q.capacity:
pspeed = 0
print(f'{self.env.now:.2f} {self.name} output buffer full!!!')
# Add the "Tailback" status to the event list
event_list.append({"Time": self.env.now, "Machine": self.name,
"State": "Tailback", "Speed": pspeed,
"Count": self.count, "Tailback": 1})
else:
pspeed = self.speed
part = yield self.in_q.get()
yield self.out_q.put(part)
self.count += 1
# Add the "Running" status to the event list
event_list.append({"Time": self.env.now, "Machine": self.name,
"State": "Running", "Speed": pspeed,
"Count": self.count, "Tailback": 0})
except simpy.Interrupt: # This ensures that the machine breaks down
self.broken = True
pspeed = 0
yield self.env.timeout(self.repair)
print(f'{self.env.now:.2f} {self.name} is fixed')
# Add the "Repaired" event notification to the event list
event_list.append({"Time": self.env.now, "Machine": self.name,
"State": "Repaired", "Speed": pspeed,
"Count": self.count, "Tailback": 0})
self.broken = False
pspeed = self.speed
env.process(self.fail_machine())
def fail_machine(self):
"""
The machine is prone to break down every now and then.
"""
yield self.env.timeout(self.mttf)
pspeed = 0
print(f'{self.env.now:.2f} {self.name} is in failure.')
# Add the "Failure" event notification to the event list
event_list.append({"Time": self.env.now, "Machine": self.name,
"State": "Failure", "Speed": pspeed,
"Count": self.count, "Tailback": 0})
if not self.broken:
# Machine only fails if currently working.
self.process.interrupt(self.mttf)
# Generating the arrival of parts in the entry buffer to be used by machine 1
def gen_arrivals(env, entry_buffer):
"""
Start the process for each part by putting
the part in the starting buffer
"""
while True:
yield env.timeout(random.uniform(0,0.0001))
# print(f'{env.now:.2f} part has arrived')
part = object() # Too lazy to make a real part class, also isn't necessary
yield entry_buffer.put(part)
# Print the status of the number of processed parts per machine every minute
def print_status(env, machine, period):
while True:
yield env.timeout(period)
print(f"{env.now}: {machine.name} has outputted {machine.count}")
# Create environment and start the setup process
env = simpy.Environment()
bufferStart = simpy.Store(env) # Buffer with unlimited capacity
buffer1 = simpy.Store(env) # Buffer between machines with unlimited capacity
bufferEnd = simpy.Store(env) # Last buffer with unlimited capacity
# The machines __init__ starts the machine process so no env.process() is needed here
machine_1 = Machine(env, 'Machine 1', bufferStart, buffer1, speed_1, MTTF_1, repair_1)
machine_2 = Machine(env, 'Machine 2', buffer1, bufferEnd, speed_2, MTTF_2, repair_2)
# Process necessary to run all events
env.process(gen_arrivals(env, bufferStart))
env.process(print_status(env, machine_1, 1))
env.process(print_status(env, machine_2, 1))
# Dictionary to log events
event_list = [{"Time": 0.00, "Machine": "All",
"State": "Simulation start",
"Speed": 0, "Count": 0, "Tailback": 0}]
# Execute
env.run(until = time)

Asyncio wait between tasks

I would like to wait between each group of 50 tasks to respect limit of APIs requests, but the sleep doesn't work. Do you have an idea. This is my code :
nb_tasks = len(tasks)
nb_page = (nb_tasks // 50) + 1
for i in range(nb_page):
if (i + 1 * 50) < nb_tasks:
tasks_to_play = tasks[i * 50: -1]
else:
tasks_to_play = tasks[i * 50: i + 1 * 50]
print("TIME : " + str(config.time_to_reset))
asyncio.gather(*tasks_to_play)
time.sleep(config.time_to_reset)
Thank you
u need to use asyncio.sleep instead of time.sleep, but your logic here doesn't exactly accomplish your goal, for example if the endpoint has a rate limit of 50 per second, but your asyncio.gather(...) takes 5 seconds to complete, not only did u miss out on 200 extra requests u could have scheduled, but even after they complete you will sleep and waste more time before making more requests
u could schedule them in blocks like u did with a gather but i think simply spreading them out with constant gaps and gathering the results is much cleaner i.e.
RATE_LIMIT_PER_SECOND = 50
async def main():
many_tasks = ...
pending_tasks = list()
for task in many_tasks:
pending_tasks.append(asyncio.create_task(task))
await asyncio.sleep(1 / RATE_LIMIT_PER_SECOND)
results = await asyncio.gather(*pending_tasks)
print(results)
asyncio.run(main())

Python 3 verification script not checking properly

I've been working on a python script and am having issues with some verification's I set up. I have this procedure file that has a function that uses a order number and a customer number to check some past history about the customers orders. Ive been testing live on our server and I keep failing the last if statement. The order number and customer number Im using does have more than one order and some are over 60 days so it should pass the test but it doesnt. Ive been looking over my code and I just cant see what could be causing this
edit: here are the print results of current and retrieved timestamps:
current_timestamp = 1531849617.921927
retrieved_timestamp = 1489622400
two_month_seconds = 5184000
one_month_seconds = 2592000
Python3
from classes import helper
from classes import api
from classes import order
from procedures import orderReleaseProcedure
import time
import datetime
import re
def verifyCustomer(customer_id, order_id):
self_helper = helper.Helper()
customer_blocked_reasons = self_helper.getConfig('customer_blocked_reasons')
order_statuses = self_helper.getConfig('order_statuses')
customer_is_blocked = False
self_api = api.Api()
self_order =order.Order(order_id)
status = {
'success' : 0,
'message' :'verify_payment_method'
}
results = self_api.which_api('orders?customer_id={}'.format(customer_id))
order_count = results['total_count']
if order_count > 1:
for result in results['orders']:
order_status_info= self_api.which_api('order_statuses/%d' % result['order_status_id'])
for customer_blocked_reason in customer_blocked_reasons:
if customer_blocked_reason in order_status_info['name']:
customer_is_blocked = True
order_id = 0
order_date = result['ordered_at']
two_month_seconds = (3600 * 24) * 60
one_month_seconds = (3600 * 24) * 30
stripped_date = order_date[:order_date.find("T")]
current_timestamp = time.time()
retrieved_timestamp = int(datetime.datetime.strptime(stripped_date, '%Y-%m-%d').strftime("%s"))
if retrieved_timestamp > (current_timestamp - one_month_seconds) and not customer_is_blocked:
status['success'] = 1
status['message'] = "Customer Verified with orders older than 30 days and no blocking reasons"
print(' 30 day check was triggered ')
print(status)
break
elif customer_is_blocked:
status_change_result = self_order.update_status(order_statuses['order_hold_manager_review'])
status['success'] = 1
status['message'] = "Changed order status to Order Hold - Manager Review"
print(' Customer block was triggered ')
print(status_change_result)
break
elif not retrieved_timestamp < (current_timestamp - two_month_seconds):
status['success'] = 0
status['message'] = "There is more than 1 order, and none are greater than 60 days, we need to check manually"
print(' 60 day check was triggered ')
print(status)
break
return status

Python + simPy: name 'move' is not defined

I am making a simulation using python 2.6 + Simpy, about the subway system. here's my code:
import sys
import random
from math import*
from math import ceil, log
from random import*
from random import random, uniform, seed, expovariate
from SimPy import*
from SimPy.Simulation import*
from math import ceil, log
totalusuarios = 0
cantgrupos=0
def triangulo(inf,sup,moda):
return random.triangular((inf),(sup),(moda))
def geometric(q):
if q == 1.0:
return 1
U = 1.0 - random.random()
G = int(ceil(log(U) / log(1.0 - q)))
return G
# A class that represents the process of generation of Groups (arrivals)
class Generador(Process):
def generar(self, prob,interarribo, porc_torniq, porc_taq, porc_maq, min, max, moda, tsertaq, tsertor, tsermaq, loncal):
global totalusuarios
global cantgrupos
totalusuarios=0
cantgrupos=0
while True:
size_g = geometric (prob)
if (now()>loncal):
cantgrupos+=1
totalusuarios=totalusuarios+size_g
for j in range (size_g):
c = Customer(name = "Usuario%02d"%(j,))
q = uniform (0,1)
##******************the userr go to the tourniquet-------------------------
if (q<=porc_torniq): #the userr go to the tourniquet
activate(c,c.go_torn(min=min, max=max, moda=moda, tsertor=tsertor)) #el cliente se desplaza
##******************the user walks to buy ticket on the office-------------------------
if (q>porc_torniq and q<=porc_torniq+porc_taq): #user go to ticket station to buy
activate(c,c.go_tickets(min, max, moda, tsertaq=tsertaq, tsertor=tsertor))
##******************the user walks to buy ticket machines-------------------------
if (q>porc_torniq+porc_taq): #user go to machines
activate(c,c.go_machines(min= min, max=max, moda=moda, tsermaq=tsermaq, tsertor=tsertor))
t = expovariate(interarribo) #time between groups of users
yield hold, self, t
class Customer(Process):
def move(self, min, max ,moda):
t1= triangulo(min_, max_, moda_)
yield hold, self,t1
def go_torn(self, min, max ,moda, tsertor):
move(min, max, moda)
yield request, self, torniquete
t2= expovariate(tsertor)
yield hold, self, t2
yield release, self, torniquete
def go_tickets(self, min, max ,moda, tsertaq, tsertor):
move(min, max, moda)
yield request, self, taquilla
t3= expovariate(tsertaq)
yield hold, self, t3
yield release, self, taquilla
go_torn(self, min, max,moda, tsertor)
def go_machines(self, min, max ,moda, tsermaq, tsertor):
move(min, max, moda)
yield request, self, taquilla
t4= expovariate(tsermaq)
yield hold, self, t4
yield release, self, taquilla
go_torn(self, min, max ,moda, tsertor)
## Experiment data ------------------------------
MedGru= 2.0
p= 1/MedGru
TasGru= 5.0
LonCor = 24.0
CanCor= 30
CanTor = 2
CanTaq=2
CanMaq=2
PorTor= 60.0/100.0
PorTaq= 20.0/100.0
PorMaq=20.0/100.0
MinDes= 0.1
MaxDes= 0.2
LonCal= 2.0*60
ModaDes= 0.15
TSerTaq= 1/0.35
TSerTor=1/0.1
TSerMaq= 1/0.5
## Model/Experiment ------------------------------
torniquete = Resource(capacity=CanTor, monitored=True, monitorType= Monitor)
maquina = Resource(capacity=CanMaq, monitored=False)
taquilla = Resource(capacity=CanTaq, monitored=False)
def simulate_():
generador = Generador(name="Grupo")
initialize() #inicializa el reloj de simulacion
activate(generador,generador.generar(p, TasGru,PorTor, PorTaq, PorMaq,
MinDes,MaxDes ,ModaDes, TSerTaq, TSerTor, TSerMaq, LonCal ))
simulate(until=60*LonCor)
for i in range(CanCor):
simulate_()
print "Groups:",cantgrupos, "Users:",totalusuarios
The code consists of 4 functions in the User class scroll, which uses a triangular distribution to simulate displacement of a passenger inside the station, of the entrance to any area (ticket office, machines or tourniquets) and from one area to another, has a
triangularly distributed random term with parameters min, mode and Max minutes.
The time it takes to serve each passenger in a ticket office is a will distributed
TSerTaq exponentially with a half minutes. Each passenger using a vending machine
ticket is occupied during a random time exponentially distributed with mean TSerMaq
minutes. Pass through a turnstile each passenger takes a random time distributed
TSerTor exponentially with a half minutes.
When I try to run my code, it tells me the following message:
C:\Documents and Settings>python llegada.py
Traceback (most recent call last):
File "llegada.py", line 111, in <module>
simulate_()
File "llegada.py", line 109, in simulate_
simulate(until=60*LonCor)
File "C:\Python26\SimPy\Globals.py", line 39, in simulate
return sim.simulate(until = until)
File "C:\Python26\SimPy\Simulation.py", line 689, in simulate
a = nextev()
File "C:\Python26\SimPy\Simulation.py", line 408, in _nextev
resultTuple = nextEvent._nextpoint.next()
File "llegada.py", line 65, in go_tickets
move(min, max, moda)
NameError: global name 'move' is not defined
I do not understand what I'm doing wrong and why I move indicates that the object is not defined. Some help please
You want self.move() not move(). move() would be a top-level function in the module, hence Python's complaint about not finding it as a global name; self.move() is a method on your class instance, which is what you actually have.
All your other method calls need the self. prepended too.

Categories

Resources