I am trying to create a bracket order that that will exit 1/2 the position at target 1 and the 2nd half at target 2. I am able to generate the orders, however, the quantity for both of the targets is the entire position amount rather than 1/2. For instance, when placing an order to buy 200 SPY the profit taking orders should have a quantity of 100 each, not 200. However, when I run my code this is the order displayed in TWS:
I am running:
Python 3.8.2 (default, May 6 2020, 09:02:42) [MSC v.1916 64 bit (AMD64)]
ib_insync 0.9.61
TWS Build 979.4t
Java Version: 1.8.0_152, OS: Windows 10 (amd64, 10.0)
Any ideas on how to solve this issue?
Please run this code to reproduce the situation:
from ib_insync import *
import time
from typing import NamedTuple
class BracketOrderTwoTargets(NamedTuple):
parent: Order
takeProfit: Order
takeProfit2: Order
stopLoss: Order
#modified from this post, https://groups.io/g/insync/message/3135
def bracketStopLimitOrderTwoTargets(
action: str, quantity: float, stopPrice: float,
limitPrice: float, takeProfitPrice1: float, takeProfitPrice2: float,
stopLossPrice: float, **kwargs) -> BracketOrderTwoTargets:
"""
Create a limit order that is bracketed by 2 take-profit orders and
a stop-loss order. Submit the bracket like:
Args:
action: 'BUY' or 'SELL'.
quantity: Size of order.
stopPrice: Stop Price for stopLimit entry order
limitPrice: Limit price of entry order.
takeProfitPrice1: 1st Limit price of profit order.
takeProfitPrice2: 2nd Limit price of profit order.
stopLossPrice: Stop price of loss order.
StopLimitOrder(action, totalQuantity, lmtPrice, stopPrice, **kwargs)
"""
assert action in ('BUY', 'SELL')
reverseAction = 'BUY' if action == 'SELL' else 'SELL'
parent = StopLimitOrder(
action, quantity, limitPrice, stopPrice,
orderId=ib.client.getReqId(),
transmit=False,
outsideRth=True,
**kwargs)
takeProfit1 = LimitOrder(
action=reverseAction, totalQuantity=quantity/2, lmtPrice=takeProfitPrice1,
orderId=ib.client.getReqId(),
transmit=False,
parentId=parent.orderId,
outsideRth=True,
**kwargs)
takeProfit2 = LimitOrder(
action=reverseAction, totalQuantity=quantity/2, lmtPrice=takeProfitPrice2,
orderId=ib.client.getReqId(),
transmit=False,
parentId=parent.orderId,
outsideRth=True,
**kwargs)
stopLoss = StopOrder(
reverseAction, quantity, stopLossPrice,
orderId=ib.client.getReqId(),
transmit=True,
parentId=parent.orderId,
outsideRth = True,
**kwargs)
return BracketOrderTwoTargets(parent, takeProfit1, takeProfit2, stopLoss)
ib = IB()
client_id = int(time.time()) # gets second since epoch. No need for the milliseconds, so round to int
ib.connect('127.0.0.1', 7497, clientId=client_id, timeout=10)
contract = Stock('SPY', exchange='SMART', currency='USD')
[ticker] = ib.reqTickers(contract)
contract_price = ticker.marketPrice()
high_bracket = bracketStopLimitOrderTwoTargets(action='BUY', quantity=200, stopPrice=contract_price+18,
limitPrice=contract_price+20,
takeProfitPrice1=contract_price+30,
takeProfitPrice2=contract_price+45,
stopLossPrice=contract_price-5)
for order in high_bracket:
order_res = ib.placeOrder(contract=contract, order=order)
print(order_res)
A different way to do it is using the scale fields attached to the profit taker. I dont know what the API looks like, but check it out in the TWS ui first. In the UI, you will see - Initial component, scale component and Scale price. You can then have the initial component of 100, and if you want the subsequent lots to be different, specify that size in the scale component.
Related
I am trying to get the number of lost clients per month. The code I'm using for the measure is set forth next:
`LostClientsRunningTotal =
VAR currdate = MAX('Date'[Date])
VAR turnoverinperiod=[Turnover]
VAR clients=
ADDCOLUMNS(
Client,
"Turnover Until Now",
CALCULATE([Turnover],
DATESINPERIOD(
'Date'[Date],
currdate,
-1,
MONTH)),
"Running Total Turnover",
[RunningTotalTurnover]
)
VAR lostclients=
FILTER(clients,
[Running Total Turnover]>0 &&
[Turnover Until Now]=0)
RETURN
IF(turnoverinperiod>0,
COUNTROWS(lostclients))`
The problem is that I'm getting the running total and the result it returns is the following:
enter image description here
What I need is the lost clients per month so I tried to use the dateadd function to get the lost clients of the previous month and then subtract the current.
The desired result would be, for Nov-22 for instance, 629 (December running total) - 544 (November running total) = 85.
For some reason the **dateadd **function is not returning the desired result and I can't make head or tails of it.
Can you tell me how should I approach this issue please? Thank you in advance.
So I've got a fairly large optimization problem and I'm trying to solve it within a sensible amount of time.
Ive set it up as:
import pulp as pl
my_problem = LpProblem("My problem",LpMinimize)
# write to problem file
my_problem.writeLP("MyProblem.lp")
And then alternatively
solver = CPLEX_CMD(timeLimit=1, gapRel=0.1)
status = my_problem .solve(solver)
solver = pl.apis.CPLEX_CMD(timeLimit=1, gapRel=0.1)
status = my_problem .solve(solver)
path_to_cplex = r'C:\Program Files\IBM\ILOG\CPLEX_Studio1210\cplex\bin\x64_win64\cplex.exe' # and yes this is the actual path on my machine
solver = pl.apis.cplex_api.CPLEX_CMD(timeLimit=1, gapRel=0.1, path=path_to_cplex)
status = my_problem .solve(solver)
solver = pl.apis.cplex_api.CPLEX_CMD(timeLimit=1, gapRel=0.1, path=path_to_cplex)
status = my_problem .solve(solver)
It runs in each case.
However, the solver does not repond to the timeLimit or gapRel instructions.
If I use timelimit it does warn this is depreciated for timeLimit. Same for fracgap: it tells me I should use relGap. So somehow I am talking to the solver.
However, nor matter what values i pick for timeLimit and relGap, it always returns the exact same answer and takes the exact same amount of time (several minutes).
Also, I have tried alternative solvers, and I cannot get any one of them to accept their variants of time limits or optimization gaps.
In each case, the problem solves and returns an status: optimal message. But it just ignores the time limit and gap instructions.
Any ideas?
out of the zoo example:
import pulp
import cplex
bus_problem = pulp.LpProblem("bus", pulp.LpMinimize)
nbBus40 = pulp.LpVariable('nbBus40', lowBound=0, cat='Integer')
nbBus30 = pulp.LpVariable('nbBus30', lowBound=0, cat='Integer')
# Objective function
bus_problem += 500 * nbBus40 + 400 * nbBus30, "cost"
# Constraints
bus_problem += 40 * nbBus40 + 30 * nbBus30 >= 300
solver = pulp.CPLEX_CMD(options=['set timelimit 40'])
bus_problem.solve(solver)
print(pulp.LpStatus[bus_problem.status])
for variable in bus_problem.variables():
print ("{} = {}".format(variable.name, variable.varValue))
Correct way to pass solver option as dictionary
pulp.CPLEX_CMD(options={'timelimit': 40})
#Alex Fleisher has it correct with pulp.CPLEX_CMD(options=['set timelimit 40']). This also works for CBC using the following syntax:
prob.solve(COIN_CMD(options=['sec 60','Presolve More','Multiple 15', 'Node DownFewest','HEUR on', 'Round On','PreProcess Aggregate','PassP 10','PassF 40','Strong 10','Cuts On', 'Gomory On', 'CutD -1', 'Branch On', 'Idiot -1', 'sprint -1','Reduce On','Two On'],msg=True)).
It is important to understand that the parameters, and associated options, are specific to a solver. PuLP seems to be calling CBC via the command line so an investigation of those things is required. Hope that helps
From replacement data table (below on the image), I am trying to incorporate the solbox product replace in time series data format(above on the image). I need to extract out the number of consumers per day from the information.
What I need to find out:
On a specific date, which number of solbox product was active
On a specific date, which number of solbox product (which was a consumer) was active
I have used this line of code in excel but cannot implement this on python properly.
=SUMPRODUCT((Record_Solbox_Replacement!$O$2:$O$1367 = "consumer") * (A475>=Record_Solbox_Replacement!$L$2:$L$1367)*(A475<Record_Solbox_Replacement!$M$2:$M$1367))
I tried in python -
timebase_df['date'] = pd.date_range(start = replace_table_df['solbox_started'].min(), end = replace_table_df['solbox_started'].max(), freq = frequency)
timebase_df['date_unix'] = timebase_df['date'].astype(np.int64) // 10**9
timebase_df['no_of_solboxes'] = ((timebase_df['date_unix']>=replace_table_df['started'].to_numpy()) & (timebase_df['date_unix'] < replace_table_df['ended'].to_numpy() & replace_table_df['customer_type'] == 'customer']))
ERROR:
~\Anaconda3\Anaconda4\lib\site-packages\pandas\core\ops\array_ops.py in comparison_op(left, right, op)
232 # The ambiguous case is object-dtype. See GH#27803
233 if len(lvalues) != len(rvalues):
--> 234 raise ValueError("Lengths must match to compare")
235
236 if should_extension_dispatch(lvalues, rvalues):
ValueError: Lengths must match to compare
Can someone help me please? I can explain in comment section if I have missed something.
Is there a way for the test_ids gem to group tests such that the same softbin gets assigned? For example, here are 3 tests passed in the flow file:
func :test1, speed: 1000, vdd: :vmin
func :test2, speed: 1200, vdd: :vmin
func :test3, speed: 1000, vdd: :vmax
Would like to be able to tell test_ids gem to group by :vdd and get the following softbins assigned (assume the range is 200-299):
200, func_vmin
201, func_vmax
If I passed speed as the grouping arg I would get the following softbins:
200, func_1000
201, func_1200
The examples shown above only pass one piece of metadata but the ask would be that any combination of test metadata could be used to create the softbin group name.
thx
With no special options, the test IDs plugin will use the test name as a unique ID. In that case, tests with different names will be assigned different test numbers, bins and softbins, while tests with the same name will use the same numbers.
Sometimes, like in this case, it is desired for differently named tests to share all or some of their number allocations, and there are a few options available to control this.
Firstly, you can supply a test_id: option, this explicitly defines the ID that should be used for the test when assigning numbers, now your tests will all have the same test numbers, bins and softbins:
func :test1, speed: 1000, vdd: :vmin, test_id: :t1
func :test2, speed: 1200, vdd: :vmin, test_id: :t1
func :test3, speed: 1000, vdd: :vmax, test_id: :t1
This can be further fine-tuned by supplying number:, bin: and/or softbin: options with symbol values and these will be used as the test ID when assigning that specific number type.
For example, this will assign the softbin as you want based on vdd:
func :test1, speed: 1000, vdd: :vmin, softbin: :func_vmin
func :test2, speed: 1200, vdd: :vmin, softbin: :func_vmin
func :test3, speed: 1000, vdd: :vmax, softbin: :func_vmax
This is covered in the docs here - https://origen-sdk.org/test_ids/#Multiple_Instances_of_the_Same_Test
Use your test program interface to programatically assign the IDs based on your business rules, for example in your func method:
def func(name, options)
options[:softbin] = "func_#{options[:vdd] || :nom}".to_sym
# ...
end
It is recommended to have all of your test handlers like this func method handover to a single method to add the test to the flow - https://origen-sdk.org/origen//guides/program/interface/#Detecting_Changes_in_the_Execution_Context
That would then give you a single place to implement more global rules like using vdd. vs. speed to group by.
For example, if you wanted to group by the test type and then speed, you could do something like:
def func(name, options)
options[:softbin] = "func"
# ...
add_to_flow(my_test, options)
end
def add_to_flow(test, options)
if group_by_speed?
options[:softbin] = "#{options[:softbin]_#{options[:speed] || 1000}".to_sym
else
options[:softbin] = "#{options[:softbin]_#{options[:vdd] || :nom}".to_sym
end
# ...
end
def generate_dollar_volume_weights(close, volume):
"""
Generate dollar volume weights.
Parameters
----------
close : DataFrame
Close price for each ticker and date
volume : str
Volume for each ticker and date
Returns
-------
dollar_volume_weights : DataFrame
The dollar volume weights for each ticker and date
"""
assert close.index.equals(volume.index)
assert close.columns.equals(volume.columns)
#TODO: Implement function
dollar_volume_weights=np.cumsum(close * volume) / np.cumsum(volume)
return dollar_volume_weights
project_tests.test_generate_dollar_volume_weights(generate_dollar_volume_weights)#testing the function
So my results are as below
INPUT close:
XHNP FCUB ZYRP
2005-09-09 35.44110000 34.17990000 34.02230000
2005-09-10 92.11310000 91.05430000 90.95720000
2005-09-11 57.97080000 57.78140000 58.19820000
2005-09-12 34.17050000 92.45300000 58.51070000
INPUT volume:
XHNP FCUB ZYRP
2005-09-09 9836830.00000000 17807200.00000000 8829820.00000000
2005-09-10 82242700.00000000 68531500.00000000 48160100.00000000
2005-09-11 16234800.00000000 13052700.00000000 9512010.00000000
2005-09-12 10674200.00000000 56831300.00000000 9316010.00000000
OUTPUT dollar_volume_weights:
XHNP FCUB ZYRP
2005-09-09 35.44110000 34.17990000 34.02230000
2005-09-10 86.05884636 79.32405834 82.13590461
2005-09-11 81.84884187 76.49494177 78.71200871
2005-09-12 77.57172242 82.30022612 76.22980471
EXPECTED OUTPUT FOR dollar_volume_weights:
XHNP FCUB ZYRP
2005-09-09 0.27719777 0.48394253 0.23885970
2005-09-10 0.41632975 0.34293308 0.24073717
2005-09-11 0.41848548 0.33536102 0.24615350
2005-09-12 0.05917255 0.85239760 0.08842984
I'm a beginner here, I really can't understand what I'm missing here, I think the expression for generating dollar volume weight should be some thing like dollar_volume_weights=np.cumsum(close * volume) / np.cumsum(volume)
can someone tell me why my results are different?
I think you should do close * volume and divide by the sum (np.sum(x, axis=1)) of volume * close and specify the axis=1. Furthermore, you could use div() function for this division and specify axis=0.
I agree with #joseph here. I assume you are talking about the Smart Beta project of Udacity. Their example in the prior cell is misleading and that confused the heck out of me. Anyway, the way I solved it is like the following:
def generate_dollar_volume_weights(close, volume):
"""
Generate dollar volume weights.
Parameters
----------
close : DataFrame
Close price for each ticker and date
volume : str
Volume for each ticker and date
Returns
-------
dollar_volume_weights : DataFrame
The dollar volume weights for each ticker and date
"""
assert close.index.equals(volume.index)
assert close.columns.equals(volume.columns)
#TODO: Implement function
print(close)
print(volume)
dollar_volume = close * volume
print(dollar_volume)
total_dollar_volume = dollar_volume.sum(axis=1)
print(total_dollar_volume)
dollar_volume_weights = dollar_volume.div(total_dollar_volume,axis=0)
print(dollar_volume_weights)
return dollar_volume_weights
project_tests.test_generate_dollar_volume_weights(generate_dollar_volume_weights)
Can you explain more how you desire to calculate dollar_volume_weights from the input to get the expected result?
To be clear what your code is doing, the followings show the logic it computes the result,
2005-09-09 close * volume / volume ...
2005-09-10 (close1*volume1) + (close2*volume2) / (volume1+volume2) ...