Trying to add liquidity on uniswap v3 via web3py - python

I'm trying this code on Ropsten, it but keeps failing:
contract_instance = w3.eth.contract(address="0xC36442b4a4522E871399CD717aBDD847Ab11FE88", abi=liq_ABI)
tx_hash = contract_instance.functions.mint(
(
'0x31F42841c2db5173425b5223809CF3A38FEde360',
'0xc778417E063141139Fce010982780140Aa0cD5Ab',
3000,
49548,
50549,
w3.toWei(0.001,'ether'),
w3.toWei(0.17,'ether'),
w3.toWei(0,'ether'),
w3.toWei(0,'ether'),
wallet_address,
round(time.time()) + 60*20,
)
).buildTransaction({
'from': wallet_address,
'chainId': 3,
'gas': 300000,
'gasPrice': w3.toWei(500, 'gwei'),
'nonce': w3.eth.getTransactionCount(wallet_address),
'value': Web3.toWei(0, 'ether')
})
print(tx_hash)
signed_tx = w3.eth.account.signTransaction(tx_hash, private_key=wallet_key)
tx = w3.eth.sendRawTransaction(signed_tx.rawTransaction)
Failed transaction: https://ropsten.etherscan.io/tx/0xc2f3d6ffff164df331dd4b46fc65dadc5dba8f135f6e13ef1cd383a73a2d0c4b

Web3-Ethereum-Defi Python library has a function called add_liquidity for Uniswap v3 that is probably what you are looking for.
Here is some example code:
fee = 3000
pool1 = deploy_pool(
web3,
deployer,
deployment=uniswap_v3,
token0=weth,
token1=usdc,
fee=fee,
)
pool2 = deploy_pool(
web3,
deployer,
deployment=uniswap_v3,
token0=usdc,
token1=dai,
fee=fee,
)
# add same liquidity amount to both pools as in SDK tests
min_tick, max_tick = get_default_tick_range(fee)
add_liquidity(
web3,
deployer,
deployment=uniswap_v3,
pool=pool1,
amount0=100_000,
amount1=100_000,
lower_tick=min_tick,
upper_tick=max_tick,
)
add_liquidity(
web3,
deployer,
deployment=uniswap_v3,
pool=pool2,
amount0=120_000,
amount1=100_000,
lower_tick=min_tick,
upper_tick=max_tick,
)
price_helper = UniswapV3PriceHelper(uniswap_v3)
# test get_amount_out, based on: https://github.com/Uniswap/v3-sdk/blob/1a74d5f0a31040fec4aeb1f83bba01d7c03f4870/src/entities/trade.test.ts#L394
for slippage, expected_amount_out in [
(0, 7004),
(5 * 100, 6670),
(200 * 100, 2334),
]:
amount_out = price_helper.get_amount_out(
10_000,
[
weth.address,
usdc.address,
dai.address,
],
[fee, fee],
slippage=slippage,
)
assert amount_out == expected_amount_out
# test get_amount_in, based on: https://github.com/Uniswap/v3-sdk/blob/1a74d5f0a31040fec4aeb1f83bba01d7c03f4870/src/entities/trade.test.ts#L361
for slippage, expected_amount_in in [
(0, 15488),
(5 * 100, 16262),
(200 * 100, 46464),
]:
amount_in = price_helper.get_amount_in(
10_000,
[
weth.address,
usdc.address,
dai.address,
],
[fee, fee],
slippage=slippage,
)
assert amount_in == expected_amount_in
See the fulle example code.
More information about Uniswap v3 and Python.

Related

How to filter data in a dictionary when the values are lists

I have the data structure below and I'd to filter this data using only Python.
bd = {
'portfolio': ['long_term', 'medium_term', 'short_term', 'long_term', 'short_term'],
'asset_type':['stock', 'etf', 'bonds', 'bonds', 'stock'],
'value':[2000, 3000, 5000, 8000, 1000]
}
My goal is to have the following result:
long_term_list = 10000 # (2000 + 8000)
stock_list = 3000 # (2000 + 1000)
bd = {
'portfolio': ['long_term', 'medium_term', 'short_term', 'long_term', 'short_term'],
'asset_type':['stock', 'etf', 'bonds', 'bonds', 'stock'],
'value':[2000, 3000, 5000, 8000, 1000]
}
long_term_list = sum([bd['value'][i] for i, x in enumerate(bd['portfolio']) if x == "long_term"])
stock_list = sum([bd['value'][i] for i, x in enumerate(bd['asset_type']) if x == "stock"])
print(long_term_list, stock_list)
Gives output
10000 3000

How to create partitions with a schedule in Dagster?

I am trying to create partitions within Dagster that will allow me to do backfills. The documentation has an example but it's to use the days of the week(which I was able to replicate). However, I am trying to create partitions with dates.
DATE_FORMAT = "%Y-%m-%d"
BACKFILL_DATE = "2021-04-01"
TODAY = datetime.today()
def get_number_of_days():
backfill_date_obj = datetime.strptime(BACKFILL_DATE, DATE_FORMAT)
delta = TODAY - backfill_date_obj
return delta
def get_date_partitions():
return [
Partition(
[
datetime.strftime(TODAY - timedelta(days=x), DATE_FORMAT)
for x in range(get_number_of_days().days)
]
)
]
def run_config_for_date_partition(partition):
date = partition.value
return {"solids": {"data_to_process": {"config": {"date": date}}}}
# ----------------------------------------------------------------------
date_partition_set = PartitionSetDefinition(
name="date_partition_set",
pipeline_name="my_pipeline",
partition_fn=get_date_partitions,
run_config_fn_for_partition=run_config_for_date_partition,
)
# EXAMPLE CODE FROM DAGSTER DOCS.
# def weekday_partition_selector(
# ctx: ScheduleExecutionContext, partition_set: PartitionSetDefinition
# ) -> Union[Partition, List[Partition]]:
# """Maps a schedule execution time to the corresponding partition or list
# of partitions that
# should be executed at that time"""
# partitions = partition_set.get_partitions(ctx.scheduled_execution_time)
# weekday = ctx.scheduled_execution_time.weekday() if ctx.scheduled_execution_time else 0
# return partitions[weekday]
# My attempt. I do not want to partition by the weekday name, but just by the date.
# Instead of returnng the partition_set, I think I need to do something else with it
# but I'm not sure what it is.
def daily_partition_selector(
ctx: ScheduleExecutionContext, partition_set: PartitionSetDefinition
) -> Union[Partition, List[Partition]]:
return partition_set.get_partitions(ctx.scheduled_execution_time)
my_schedule = date_partition_set.create_schedule_definition(
"my_schedule",
"15 8 * * *",
partition_selector=daily_partition_selector,
execution_timezone="UTC",
)
Current dagster UI has all the dates lumped together in the partition section.
Actual Results
Expected Results
What am I missing that will give me the expected results?
After talking to the folks at Dagster they pointed me to this documentation
https://docs.dagster.io/concepts/partitions-schedules-sensors/schedules#partition-based-schedules
This is so much simpler and I ended up with
#daily_schedule(
pipeline_name="my_pipeline",
start_date=datetime(2021, 4, 1),
execution_time=time(hour=8, minute=15),
execution_timezone="UTC",
)
def my_schedule(date):
return {
"solids": {
"data_to_process": {
"config": {
"date": date.strftime("%Y-%m-%d")
}
}
}
}

heroku app not running even after deployment

After typing heroku logs --tail --app "app_name"
I find the above error
NO Such Table
My database is working fine and its error-free then to it is showing this error that there is no such table
import pandas as pd
import base64
import datetime
import io
import dash
from dash.dependencies import Input, Output, State
import dash_core_components as dcc
import dash_html_components as html
import dash_table
import sqlalchemy as sa
from datetime import date, timedelta
import sqlite3
import flask
external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP.css"]
con = sqlite3.connect('C:\\Users\\Admin\\Downloads\\Python Scripts\\Next_Gresing_Date\\Grease.db', check_same_thread=False)
df = pd.read_sql_query('SELECT * FROM ABCC1;', con)
dataaa = df
### SQL Engine
disk_engine = sa.create_engine("sqlite:///C:\\Users\\Admin\\Downloads\\Python Scripts\\Next_Gresing_Date\\Grease.db")
connection = disk_engine.connect()
metadata = sa.MetaData()
SQL_table = sa.Table(
"ABCC1",
metadata,
sa.Column("Site", sa.VARCHAR),
sa.Column("Last_Greased_Date:YYYY-MM-DD", sa.TEXT),
sa.Column("Department", sa.VARCHAR),
sa.Column("Equipment_ID", sa.VARCHAR),
sa.Column("Equipment_Name", sa.VARCHAR),
sa.Column("HAC_Code", sa.VARCHAR),
sa.Column("Frequency_Schedule_Days", sa.INTEGER),
sa.Column("NEXT_Date:YYYY-MM-DD", sa.TEXT),
sa.Column("Grease_Grade", sa.VARCHAR),
sa.Column("Point", sa.INTEGER),
sa.Column("Stroke", sa.INTEGER),
sa.Column("Grease_Gun_No(gm_per_stroke)", sa.VARCHAR),
sa.Column("Quantity_Grease_In_Grams(in_one_stroke)", sa.FLOAT),
sa.Column("Total_Quantity_Grease_Used(in_gms)", sa.FLOAT),
sa.Column("Name_Of_Technicians", sa.TEXT),
sa.Column("Remarks", sa.VARCHAR),
)
disk_engine = sa.create_engine(
"sqlite:///C:\\Users\\Admin\\Downloads\\Python Scripts\\Next_Gresing_Date\\Grease.db",
connect_args={"check_same_thread": False})
connection = disk_engine.connect()
metadata = sa.MetaData()
SQL_TABLE = sa.Table(
"Scheduler",
metadata,
sa.Column("Department", sa.VARCHAR),
sa.Column("EqName", sa.VARCHAR),
sa.Column("EqId", sa.VARCHAR),
sa.Column("GreaseGrade", sa.VARCHAR),
sa.Column("Point", sa.INTEGER),
sa.Column("Stroke", sa.INTEGER),
sa.Column("gmperstroke", sa.FLOAT),
sa.Column("TotalGreaseused(ingms.)", sa.FLOAT),
sa.Column('GreaseNippleStatus', sa.TEXT),
sa.Column("Schedule.freqDays", sa.INTEGER),
sa.Column("AttendeBy", sa.VARCHAR),
sa.Column("Remark/anyabnormalitiesfound", sa.VARCHAR),
)
dss = pd.read_sql_query('SELECT * FROM Scheduler;', con)
app = dash.Dash(__name__, external_stylesheets=external_stylesheets, )
server = flask.Flask("C:\\Users\\Admin\\Downloads\\Python Scripts\\Next_Gresing_Date\\Database")
def serve_layout():
layout = html.Div([
html.H4("Next Greasing Date"),
html.H4("Upload"),
dcc.Upload(
id="upload-data",
children=html.Div(
["Drag and drop or click to select a file to upload."]
),
multiple=True,
style={
"width": "100%",
"height": "60px",
"lineHeight": "60px",
"borderWidth": "1px",
"borderStyle": "dashed",
"borderRadius": "5px",
"textAlign": "center",
"margin": "10px",
}
),
html.Div(id='output-of-upload'),
html.P([
html.Label('Choose a Department:', style={'fontSize': 18}),
dcc.Dropdown(
id='dept_input',
options=[{'label': i, 'value': i} for i in dataa['Department'].unique()],
style={'height': '30px', 'width': '300px'}
)], className="three columns"),
html.P([
html.Label('Choose Date:Day-Month-Year', style={'fontSize': 18}),
dcc.DatePickerSingle(
id="single",
month_format='MMMM Y',
placeholder='DD-MM-YYYY',
with_portal=True,
clearable=True,
display_format="DD-MM-YYYY",
)], ),
html.P([
html.Label('Next Greasing Dates', style={'fontSize': 18}),
html.Div([
dash_table.DataTable(
id='next_greasing_dates', sort_action="native", sort_mode="multi",
columns=[{"name": i, "id": i} for i in ['Site', 'Last_Greased_Date:YYYY-MM-DD', 'Department',
'Equipment_ID', 'Equipment_Name',
'HAC_Code', 'Frequency_Schedule_Days',
'NEXT_Date:YYYY-MM-DD', 'Grease_Grade', 'Point', 'Stroke',
'Grease_Gun(gm_per_stroke)',
'Quantity_Grease_In_Grams(in_one_stroke)',
'Total_Quantity_Grease_Used(in_gms)',
'Name_Of_Technicians', 'Remarks']],
export_format='csv',
export_columns="all",
export_headers='display',
merge_duplicate_headers=True,
style_cell={'textAlign': 'left'},
style_cell_conditional=[
{'if': {'column_id': 'Equipment_ID'},
'textAlign': 'center'},
{'if': {'column_id': 'HAC_CODE'},
'textAlign': 'center'},
{'if': {'column_id': 'Last_Greased_Date:YYYY-MM-DD'},
'textAlign': 'center'},
{'if': {'column_id': 'Frequency_Schedule_Days'},
'textAlign': 'center'},
{'if': {'column_id': 'NEXT_Date:YYYY-MM-DD'},
'textAlign': 'center'},
{'if': {'column_id': 'Point'},
'textAlign': 'center'},
{'if': {'column_id': 'Quantity_Grease_In_Grams(in_one_stroke)'},
'textAlign': 'center'},
{'if': {'column_id': 'Stroke'},
'textAlign': 'center'},
{'if': {'column_id': 'Date:Year-Month-Date'},
'width': '40%'},
{'if': {'column_id': 'NEXT_Date:YYYY-MM-DD'},
'width': '40%'}],
style_header={
'backgroundColor': 'rgb(230, 230, 230)',
'fontWeight': 'bold'},
style_table={'overflowX': 'scroll'}, )
])
]),
]
)
return layout
def parse_contents(contents, filename, date):
content_type, content_string = contents.split(',')
decoded = base64.b64decode(content_string)
try:
if 'csv' in filename:
# Assume that the user uploaded a CSV file
dg = pd.read_csv(io.StringIO(decoded.decode('utf-8'))).to_sql('ABCC1', con, if_exists='append', index=False)
elif 'xls' in filename:
# Assume that the user uploaded an excel file
dg = pd.read_excel(io.BytesIO(decoded)).to_sql('ABCC1', con, if_exists='append', index=False)
except Exception as e:
print(e)
return html.Div([
'There was an error processing this file.'
])
return html.Div([
html.H5(filename),
html.H6(datetime.datetime.fromtimestamp(date))
])
# adding one column in dataa by setting its value to 0
dataaa['freq'] = 0
dataaa['grade'] = 0
dataaa['qty'] = 0
dataaa['stroke'] = 0
dataaa['point'] = 0
dataaa['man'] = 0
dataaa['depty'] = 0
# converting date object to date time format
dataaa['Last_Greased_Date:YYYY-MM-DD'] = pd.to_datetime(dataaa['Last_Greased_Date:YYYY-MM-DD'])
dataaa['Last_Greased_Date:YYYY-MM-DD'].dt.strftime("%Y-%m-%d")
dataa = dataaa
dataa['date'] = pd.to_datetime(dataa['Last_Greased_Date:YYYY-MM-DD'])
# checking eq id and eq name in scheduler dataset and then filling the respective new columns
for i in range(0, len(dss)):
m = dss['EqName'][i]
n = dss['EqId'][i]
if (dataa['Equipment_ID'] == n).any():
dataa['freq'][(dataa['Equipment_Name'] == m) & (dataa['Equipment_ID'] == n)] = dss['Schedule.freqDays'][i]
dataa['depty'][(dataa['Equipment_Name'] == m) & (dataa['Equipment_ID'] == n)] = dss['Department'][i]
dataa['grade'][(dataa['Equipment_Name'] == m) & (dataa['Equipment_ID'] == n)] = dss['GreaseGrade'][i]
dataa['point'][(dataa['Equipment_Name'] == m) & (dataa['Equipment_ID'] == n)] = dss['Point'][i]
dataa['qty'][(dataa['Equipment_Name'] == m) & (dataa['Equipment_ID'] == n)] = dss['TotalGreaseused(ingms.)'][i]
dataa['stroke'][(dataa['Equipment_Name'] == m) & (dataa['Equipment_ID'] == n)] = dss['Stroke'][i]
dataa['man'][(dataa['Equipment_Name'] == m) & (dataa['Equipment_ID'] == n)] = dss['AttendeBy'][i]
else:
dataa['freq'][(dataa['Equipment_Name'] == m) & (dataa['HAC_Code'] == n)] = dss['Schedule.freqDays'][i]
dataa['depty'][(dataa['Equipment_Name'] == m) & (dataa['HAC_Code'] == n)] = dss['Department'][i]
dataa['grade'][(dataa['Equipment_Name'] == m) & (dataa['HAC_Code'] == n)] = dss['GreaseGrade'][i]
dataa['point'][(dataa['Equipment_Name'] == m) & (dataa['HAC_Code'] == n)] = dss['Point'][i]
dataa['qty'][(dataa['Equipment_Name'] == m) & (dataa['HAC_Code'] == n)] = dss['TotalGreaseused(ingms.)'][i]
dataa['stroke'][(dataa['Equipment_Name'] == m) & (dataa['HAC_Code'] == n)] = dss['Stroke'][i]
dataa['man'][(dataa['Equipment_Name'] == m) & (dataa['HAC_Code'] == n)] = dss['AttendeBy'][i]
# Sorting date in decending order inorder to get last greasing date of every equipment
dataa['Last_Greased_Date:YYYY-MM-DD'] = dataa['Last_Greased_Date:YYYY-MM-DD'].astype(str)
d = dataa.sort_values("Last_Greased_Date:YYYY-MM-DD", ascending=False)
d.reset_index(inplace=True)
# droping the duplicates of eqid and eqname and keeping them first inorder to get the last graesing dates of equipments
di = d.drop_duplicates(["Equipment_Name", "HAC_Code"], keep='first')
di = d.drop_duplicates(["Equipment_ID", "Equipment_Name"], keep='first')
di.drop('index', axis=1, inplace=True)
di.reset_index(inplace=True)
app.layout = serve_layout
#app.callback(Output('next_greasing_dates', 'data'),
[Input('single', 'date'),
Input('dept_input', 'value')])
def ngrease_table(datee, dept):
data = di[di['Department'] == dept]
data1 = data[['Site', 'Last_Greased_Date:YYYY-MM-DD', 'Department', 'Equipment_ID', 'Equipment_Name',
'HAC_Code', 'Frequency_Schedule_Days', 'NEXT_Date:YYYY-MM-DD',
'Grease_Grade', 'Point', 'Stroke', 'Grease_Gun_No(gm_per_stroke)',
'Quantity_Grease_In_Grams(in_one_stroke)', 'Total_Quantity_Grease_Used(in_gms)',
'Name_Of_Technicians', 'Remarks', 'freq', 'date', "grade", 'point', "qty", "stroke", "man"]]
data1.reset_index(inplace=True)
data1.drop('index', axis=1, inplace=True)
yl = int(
datee.split('-')[0]) # splitting the end date in year month and date and putting it in separate index 0,1,2
ml = int(datee.split('-')[1])
dl = int(datee.split('-')[2])
df2 = pd.DataFrame()
for i in range(0, len(data1)):
# splitting last date in year month and date and putting it in separate index 0,1,2
y = int(data1['Last_Greased_Date:YYYY-MM-DD'][i].split('-')[0])
m = int(data1['Last_Greased_Date:YYYY-MM-DD'][i].split('-')[1])
d = int(data1['Last_Greased_Date:YYYY-MM-DD'][i].split('-')[2])
if data1['freq'][i] != 0: # giving freq not = 0 because it gives us error
# subtracting end date with the last date to get number of days in between and then divding it by freq
# to get the quotient so we can come to know that how many times it should get greased
# for example if the diff betwen dates are 30 and the and the freq we get is 10 when diveded we get 3days
# that means 3 times we can apply greasing between to selected dates
p = (date(year=yl, month=ml, day=dl) - date(year=y, month=m, day=d)) // data1['freq'][i]
# making new data frame changing column names adding new column NEXT_Date
# which is initially 0 and then multiplying p.days to it ie. 3days will get
# multiplied to it and number of rows will be 3
df1 = pd.DataFrame({'Site': [data1['Site'][i]] * p.days,
'Last_Greased_Date:YYYY-MM-DD': [data1['Last_Greased_Date:YYYY-MM-DD'][i]] * p.days,
'Department': [data1['Department'][i]] * p.days,
'Equipment_ID': [data1['Equipment_ID'][i]] * p.days,
'Equipment_Name': [data1['Equipment_Name'][i]] * p.days,
'HAC_Code': [data1['HAC_Code'][i]] * p.days,
'Frequency_Schedule_Days': [data1['freq'][i]] * p.days,
'NEXT_Date:YYYY-MM-DD': [0] * p.days,
"Grease_Grade": [data1["grade"][i]] * p.days,
"Point": [data1["point"][i]] * p.days,
"Stroke": [data1["stroke"][i]] * p.days,
"Quantity_Grease_In_Grams(in_one_stroke)": [data1["qty"][i]] * p.days, })
for j in range(0, len(df1)): # it will take lenght according to df1
# now adding date with freq and saving it in NEXT_Date
df1['NEXT_Date:YYYY-MM-DD'][j] = data1.date[i] + (timedelta(days=int(data1['freq'][i] * (j + 1))))
df2 = pd.concat([df1, df2])
# above it wil give all the dates like last date to end date
# therefore, here we have given start date so that it can only display from selected start date to end date
df3 = df2[df2['NEXT_Date:YYYY-MM-DD'] >= datetime.datetime.strptime(datee, '%Y-%m-%d')]
df3.sort_values(by='NEXT_Date:YYYY-MM-DD', inplace=True)
# here the seconds with the date will get cut off
df3['NEXT_Date:YYYY-MM-DD'] = df3['NEXT_Date:YYYY-MM-DD'].apply(lambda x: str(x.date()))
df3.reset_index(inplace=True)
df3.drop('index', axis=1, inplace=True)
con = sa.create_engine('sqlite:///C:\\Users\\Admin\\Downloads\\Python Scripts\\Next_Gresing_Date\\Grease.db')
df3.to_sql('Next_Datee', con, if_exists='append', index=False)
return df3.to_dict('records')
#app.callback(Output('output-of-upload', 'children'),
[Input('upload-data', 'contents')],
[State('upload-data', 'filename'),
State('upload-data', 'last_modified')])
def update_output(list_of_contents, list_of_names, list_of_dates):
if list_of_contents is not None:
children = [
parse_contents(contents, filename, date) for contents, filename, date in
zip(list_of_contents, list_of_names, list_of_dates)]
return children
if __name__ == "__main__":
app.run_server()
above is my code
Requirement text
Click==7.0
dash==1.9.0
dash-core-components==1.8.0
dash-html-components==1.0.2
dash-renderer==1.2.4
dash-table==4.6.0
Flask==1.1.1
Flask-Compress==1.4.0
Flask-SeaSurf==0.2.2
future==0.18.2
gunicorn==20.0.4
itsdangerous==1.1.0
Jinja2==2.11.1
MarkupSafe==1.1.1
numpy==1.18.1
pandas==1.0.1
plotly==4.5.0
python-dateutil==2.8.1
pytz==2019.3
retrying==1.3.3
six==1.14.0
SQLAlchemy==1.3.13
ua-parser==0.9.0
Werkzeug==1.0.0
This is my gitignore file
venv
*.pyc
.DS_Store
.env
This is my Procfile
web: gunicorn app:server
You hardcoded your sql database:
con = sqlite3.connect('C:\\Users\\Admin\\Downloads\\Python Scripts\\Next_Gresing_Date\\Grease.db', check_same_thread=False)
disk_engine = sa.create_engine("sqlite:///C:\\Users\\Admin\\Downloads\\Python Scripts\\Next_Gresing_Date\\Grease.db")
This path is not available on Linux.
You can build a relative path like this:
import os
dirname = os.path.dirname(__file__)
filename = os.path.join(dirname, '../relative/path/to/file/you/want.db')
With .. you can go a folder structure up. os.path.dirname(__file__) returns the path of the folder the python file is currently in.

How to add customs value in python-fedex for international shipments?

I'm using the python-fedex module as a light wrapper for the FedEx SOAP API. As part of this, I'm trying to set up a basic example of an international shipment, but I'm getting stuck with the following error message:
fedex.base_service.FedexError: Customs Value is required. (Error code: 2033)
I believe I need to add the products I ship as commodities, incl. their customs value - but I struggle to get this to work. I found this link with some guidance (from C#), but I was unable to get it to work in Python. Any inputs are appreciated!
My code is below:
# !/usr/bin/env python
"""
This example shows how to create a shipment and generate a waybill as output. The variables populated below
represents the minimum required values. You will need to fill all of these, or
risk seeing a SchemaValidationError exception thrown.
Near the bottom of the module, you'll see some different ways to handle the
label data that is returned with the reply.
"""
import logging
import binascii
import datetime
import sys, os
from example_config import CONFIG_OBJ
from fedex.services.ship_service import FedexProcessShipmentRequest
# What kind of file do you want this example to generate?
# Valid choices for this example are PDF, PNG
GENERATE_IMAGE_TYPE = 'PDF'
# Un-comment to see the response from Fedex printed in stdout.
logging.basicConfig(stream=sys.stdout, level=logging.INFO)
# This is the object that will be handling our shipment request.
# We're using the FedexConfig object from example_config.py in this dir.
customer_transaction_id = "*** ShipService Request v17 using Python ***" # Optional transaction_id
shipment = FedexProcessShipmentRequest(CONFIG_OBJ, customer_transaction_id=customer_transaction_id)
# This is very generalized, top-level information.
# REGULAR_PICKUP, REQUEST_COURIER, DROP_BOX, BUSINESS_SERVICE_CENTER or STATION
shipment.RequestedShipment.DropoffType = 'BUSINESS_SERVICE_CENTER'
# See page 355 in WS_ShipService.pdf for a full list. Here are the common ones:
# STANDARD_OVERNIGHT, PRIORITY_OVERNIGHT, FEDEX_GROUND, FEDEX_EXPRESS_SAVER,
# FEDEX_2_DAY, INTERNATIONAL_PRIORITY, SAME_DAY, INTERNATIONAL_ECONOMY
shipment.RequestedShipment.ServiceType = 'INTERNATIONAL_PRIORITY'
# What kind of package this will be shipped in.
# FEDEX_BOX, FEDEX_PAK, FEDEX_TUBE, YOUR_PACKAGING, FEDEX_ENVELOPE
shipment.RequestedShipment.PackagingType = 'FEDEX_ENVELOPE'
# Shipper contact info.
shipment.RequestedShipment.Shipper.Contact.PersonName = 'Shipper Name'
shipment.RequestedShipment.Shipper.Contact.CompanyName = 'Shipper Company'
shipment.RequestedShipment.Shipper.Contact.PhoneNumber = '004512345678'
# Shipper address.
shipment.RequestedShipment.Shipper.Address.StreetLines = ['Shipper Address']
shipment.RequestedShipment.Shipper.Address.City = 'City'
shipment.RequestedShipment.Shipper.Address.StateOrProvinceCode = ''
shipment.RequestedShipment.Shipper.Address.PostalCode = '8270'
shipment.RequestedShipment.Shipper.Address.CountryCode = 'DK'
shipment.RequestedShipment.Shipper.Address.Residential = False
# Recipient contact info.
shipment.RequestedShipment.Recipient.Contact.PersonName = 'US customer X'
shipment.RequestedShipment.Recipient.Contact.CompanyName = 'US company X'
shipment.RequestedShipment.Recipient.Contact.PhoneNumber = '0123456789'
# Recipient address
shipment.RequestedShipment.Recipient.Address.StreetLines = ['668 MURRAY AVE SE']
shipment.RequestedShipment.Recipient.Address.City = 'ROANOKE'
shipment.RequestedShipment.Recipient.Address.StateOrProvinceCode = 'VA'
shipment.RequestedShipment.Recipient.Address.PostalCode = '24013'
shipment.RequestedShipment.Recipient.Address.CountryCode = 'US'
# This is needed to ensure an accurate rate quote with the response. Use AddressValidation to get ResidentialStatus
shipment.RequestedShipment.Recipient.Address.Residential = False
shipment.RequestedShipment.EdtRequestType = 'NONE'
# Senders account information
shipment.RequestedShipment.ShippingChargesPayment.Payor.ResponsibleParty.AccountNumber = CONFIG_OBJ.account_number
# Who pays for the shipment?
# RECIPIENT, SENDER or THIRD_PARTY
shipment.RequestedShipment.ShippingChargesPayment.PaymentType = 'SENDER'
# Specifies the label type to be returned.
# LABEL_DATA_ONLY or COMMON2D
shipment.RequestedShipment.LabelSpecification.LabelFormatType = 'COMMON2D'
# Specifies which format the label file will be sent to you in.
# DPL, EPL2, PDF, PNG, ZPLII
shipment.RequestedShipment.LabelSpecification.ImageType = GENERATE_IMAGE_TYPE
# To use doctab stocks, you must change ImageType above to one of the
# label printer formats (ZPLII, EPL2, DPL).
# See documentation for paper types, there quite a few.
shipment.RequestedShipment.LabelSpecification.LabelStockType = 'PAPER_7X4.75'
# This indicates if the top or bottom of the label comes out of the
# printer first.
# BOTTOM_EDGE_OF_TEXT_FIRST or TOP_EDGE_OF_TEXT_FIRST
# Timestamp in YYYY-MM-DDThh:mm:ss format, e.g. 2002-05-30T09:00:00
shipment.RequestedShipment.ShipTimestamp = datetime.datetime.now().replace(microsecond=0).isoformat()
# BOTTOM_EDGE_OF_TEXT_FIRST, TOP_EDGE_OF_TEXT_FIRST
shipment.RequestedShipment.LabelSpecification.LabelPrintingOrientation = 'TOP_EDGE_OF_TEXT_FIRST'
# Delete the flags we don't want.
# Can be SHIPPING_LABEL_FIRST, SHIPPING_LABEL_LAST or delete
if hasattr(shipment.RequestedShipment.LabelSpecification, 'LabelOrder'):
del shipment.RequestedShipment.LabelSpecification.LabelOrder # Delete, not using.
# Create Weight, in pounds.
package1_weight = shipment.create_wsdl_object_of_type('Weight')
package1_weight.Value = 1.0
package1_weight.Units = "LB"
# Create PackageLineItem
package1 = shipment.create_wsdl_object_of_type('RequestedPackageLineItem')
# BAG, BARREL, BASKET, BOX, BUCKET, BUNDLE, CARTON, CASE, CONTAINER, ENVELOPE etc..
package1.PhysicalPackaging = 'ENVELOPE'
package1.Weight = package1_weight
# Add a signature option for the package using SpecialServicesRequested or comment out.
# SpecialServiceTypes can be APPOINTMENT_DELIVERY, COD, DANGEROUS_GOODS, DRY_ICE, SIGNATURE_OPTION etc..
package1.SpecialServicesRequested.SpecialServiceTypes = 'SIGNATURE_OPTION'
# SignatureOptionType can be ADULT, DIRECT, INDIRECT, NO_SIGNATURE_REQUIRED, SERVICE_DEFAULT
package1.SpecialServicesRequested.SignatureOptionDetail.OptionType = 'SERVICE_DEFAULT'
# This adds the RequestedPackageLineItem WSDL object to the shipment. It
# increments the package count and total weight of the shipment for you.
shipment.add_package(package1)
# If you want to make sure that all of your entered details are valid, you
# can call this and parse it just like you would via send_request(). If
# shipment.response.HighestSeverity == "SUCCESS", your shipment is valid.
# print(shipment.send_validation_request())
# Fires off the request, sets the 'response' attribute on the object.
shipment.send_request()
Your need to add more information.
Insert the following codes before shipment.send_request(), then try again.
shipment.RequestedShipment.CustomsClearanceDetail.CustomsValue.Currency = 'USD'
shipment.RequestedShipment.CustomsClearanceDetail.CustomsValue.Amount = 1.0
shipment.RequestedShipment.CustomsClearanceDetail.DutiesPayment.PaymentType = 'SENDER'
shipment.RequestedShipment.CustomsClearanceDetail.DutiesPayment.Payor.ResponsibleParty.AccountNumber = CONFIG_OBJ.account_number
commodity1 = shipment.create_wsdl_object_of_type('Commodity')
commodity1.Name = 'book'
commodity1.NumberOfPieces = 1
commodity1.Description = '1'
commodity1.CountryOfManufacture = 'CN'
commodity1.HarmonizedCode = '123456789'
commodity1.Quantity = 1.0
commodity1.QuantityUnits = 'EA'
commodity1.Weight.Value = 1.0
commodity1.Weight.Units = "LB"
commodity1.CustomsValue.Currency = 'USD'
commodity1.CustomsValue.Amount = 1.0
commodity1.UnitPrice.Currency = 'USD'
commodity1.UnitPrice.Amount = 1.0
shipment.RequestedShipment.CustomsClearanceDetail.Commodities = [commodity1]
I have added a full international shipment example below, which solves this:
"""
This example shows how to create an international shipment and generate a waybill as output.
The example takes outset in a real practical use case, where electronic trade documents are
used and an existing PDF commercial invoice is added along with product descriptions via ETD.
Further, it adds event notifications to allow for emails to be sent to the end recipient.
The script is comprised of a FedExLabelHelper class with all core functions, and a use case
example with minimal dummy data
"""
from example_config import CONFIG_OBJ
from pathlib import Path
import binascii
import datetime
from fedex.services.ship_service import FedexProcessShipmentRequest
# ----------------------------------------------------
# FedEx class for creating shipments
class FedexLabelHelper:
mCommodities = []
def __init__(self):
pass
# ----------------------------------------------------
# set overall shipment configuration
def setShipmentConfig(
self,
CONFIG_OBJ,
invoice_info,
cust_tran_id="*** ShipService Request v17 using Python ***",
dropoffType="BUSINESS_SERVICE_CENTER",
shippingPaymentType="SENDER",
labelFormatType="COMMON2D",
labelSpecificationImageType="PDF",
labelSpecificationStockType="PAPER_7X4.75",
labelPrintingOrientation="TOP_EDGE_OF_TEXT_FIRST",
LabelOrder="SHIPPING_LABEL_FIRST",
):
self.invoice_info = invoice_info
self.dropoffType = dropoffType
self.serviceType = "INTERNATIONAL_PRIORITY" if invoice_info["ShippingExpress"] == True else "INTERNATIONAL_ECONOMY"
self.mCommodities.clear()
self.CONFIG_OBJ = CONFIG_OBJ
self.shipment = FedexProcessShipmentRequest(CONFIG_OBJ, customer_transaction_id=cust_tran_id)
self.shipment.RequestedShipment.DropoffType = dropoffType
self.shipment.RequestedShipment.ServiceType = self.serviceType
self.shipment.RequestedShipment.ShippingChargesPayment.Payor.ResponsibleParty.AccountNumber = CONFIG_OBJ.account_number
self.shipment.RequestedShipment.ShippingChargesPayment.Payor.ResponsibleParty.Address.CountryCode = "DK"
self.shipment.RequestedShipment.ShippingChargesPayment.PaymentType = shippingPaymentType
labelSpecification = self.shipment.create_wsdl_object_of_type("LabelSpecification")
labelSpecification.LabelFormatType = labelFormatType
labelSpecification.LabelStockType = labelSpecificationStockType
labelSpecification.ImageType = labelSpecificationImageType
labelSpecification.LabelOrder = LabelOrder
labelSpecification.LabelPrintingOrientation = labelPrintingOrientation
self.shipment.RequestedShipment.LabelSpecification = labelSpecification
# ----------------------------------------------------
# set sender information
def setSenderInfo(self, sender):
self.shipment.RequestedShipment.Shipper.Contact.PersonName = sender["Name"]
self.shipment.RequestedShipment.Shipper.Contact.CompanyName = sender["Company"]
self.shipment.RequestedShipment.Shipper.Contact.PhoneNumber = sender["Phone"]
self.shipment.RequestedShipment.Shipper.Contact.EMailAddress = sender["Email"]
self.shipment.RequestedShipment.Shipper.Address.StreetLines = sender["Address"]
self.shipment.RequestedShipment.Shipper.Address.City = sender["City"]
self.shipment.RequestedShipment.Shipper.Address.StateOrProvinceCode = sender["Region"]
self.shipment.RequestedShipment.Shipper.Address.PostalCode = sender["Zip"]
self.shipment.RequestedShipment.Shipper.Address.CountryCode = sender["CountryCode"]
self.shipment.RequestedShipment.Shipper.Address.Residential = sender["Residential"]
ti = self.shipment.create_wsdl_object_of_type("TaxpayerIdentification")
ti.Number = sender["VAT"]
ti.TinType = "BUSINESS_NATIONAL"
self.shipment.RequestedShipment.Shipper.Tins = ti
# ----------------------------------------------------
# upload all documents (invoice and product information)
def upload_all_documents(self):
doc_ids = []
doc_ids.append(self.upload_document(self.invoice_info["InvoicePath"], "COMMERCIAL_INVOICE"))
for pdf in self.invoice_info["Pdfs"]:
doc_ids.append(self.upload_document(pdf, "OTHER"))
return doc_ids
# ----------------------------------------------------
# function for uploading documents as electronic trade documents and getting the response doc IDs
def upload_document(self, path, type):
from fedex.services.document_service import FedexDocumentServiceRequest
# specify prefix for use in attachment naming
if type == "COMMERCIAL_INVOICE":
prefix = "invoice_"
else:
prefix = "product_description_"
uploadRequest = FedexDocumentServiceRequest(self.CONFIG_OBJ)
uploadRequest.OriginCountryCode = "DK"
uploadRequest.DestinationCountryCode = self.shipment.RequestedShipment.Recipient.Address.CountryCode
uploadRequest.Usage = "ELECTRONIC_TRADE_DOCUMENTS"
clientdetails = uploadRequest.create_wsdl_object_of_type("ClientDetail")
clientdetails.AccountNumber = self.CONFIG_OBJ.account_number
clientdetails.MeterNumber = self.CONFIG_OBJ.meter_number
uploadRequest.ClientDetail = clientdetails
webAuthDetails = uploadRequest.create_wsdl_object_of_type("WebAuthenticationDetail")
webAuthDetails.ParentCredential.Key = self.CONFIG_OBJ.key
webAuthDetails.ParentCredential.Password = self.CONFIG_OBJ.password
webAuthDetails.UserCredential.Key = self.CONFIG_OBJ.key
webAuthDetails.UserCredential.Password = self.CONFIG_OBJ.password
uploadRequest.WebAuthenticationDetail = webAuthDetails
docdetails = uploadRequest.create_wsdl_object_of_type("UploadDocumentDetail")
docdetails.LineNumber = 1
docdetails.DocumentType = type
docdetails.FileName = prefix + path
fileContent = open(path, "rb").read()
fileBase64 = binascii.b2a_base64(fileContent)
docdetails.DocumentContent = fileBase64.decode("cp1250")
uploadRequest.Documents = docdetails
uploadRequest.send_request()
doc_id = uploadRequest.response.DocumentStatuses[0].DocumentId
return doc_id
# ----------------------------------------------------
# set recipient information
def setRecipientInfo(self, recipient):
self.shipment.RequestedShipment.Recipient.Contact.PersonName = recipient["Name"]
self.shipment.RequestedShipment.Recipient.Contact.CompanyName = recipient["Company"]
self.shipment.RequestedShipment.Recipient.Contact.PhoneNumber = recipient["Phone"]
self.shipment.RequestedShipment.Recipient.Contact.EMailAddress = recipient["Email"]
self.shipment.RequestedShipment.Recipient.Address.StreetLines = recipient["Address"]
self.shipment.RequestedShipment.Recipient.Address.City = recipient["City"]
self.shipment.RequestedShipment.Recipient.Address.StateOrProvinceCode = recipient["Region"]
self.shipment.RequestedShipment.Recipient.Address.PostalCode = recipient["Zip"]
self.shipment.RequestedShipment.Recipient.Address.CountryCode = recipient["CountryCode"]
self.shipment.RequestedShipment.Recipient.Address.Residential = recipient["Residential"]
ti = self.shipment.create_wsdl_object_of_type("TaxpayerIdentification")
ti.Number = recipient["VAT"]
ti.TinType = "BUSINESS_NATIONAL"
self.shipment.RequestedShipment.Recipient.Tins = ti
# ----------------------------------------------------
# add "commercial invoice" reference as the only commodity
def add_ci_commodity(self):
self.addCommodity(
cCustomsValueAmnt=self.invoice_info["Value"],
cCustomsValueCurrency=self.invoice_info["Currency"],
cWeightValue=self.invoice_info["Weight"],
cDescription="See attached commercial invoice",
cQuantity=self.invoice_info["Quantity"],
cExportLicenseNumber=self.shipment.RequestedShipment.Shipper.Tins.Number,
cPartNumber=1,
)
# ----------------------------------------------------
# add commodity to shipment (for now, just add 1 commodity to refer to attached CI)
def addCommodity(
self, cCustomsValueAmnt, cCustomsValueCurrency, cWeightValue, cDescription, cQuantity, cExportLicenseNumber, cPartNumber,
):
commodity = self.shipment.create_wsdl_object_of_type("Commodity")
commodity.NumberOfPieces = str(cQuantity)
commodity.Description = cDescription
commodity.Quantity = cQuantity
commodity.QuantityUnits = "EA"
commodity.ExportLicenseNumber = cExportLicenseNumber
commodity.PartNumber = cPartNumber
commodity.CountryOfManufacture = "DK"
mCustomsValue = self.shipment.create_wsdl_object_of_type("Money")
mCustomsValue.Amount = cCustomsValueAmnt
mCustomsValue.Currency = cCustomsValueCurrency
commodity.CustomsValue = mCustomsValue
commodity_weight = self.shipment.create_wsdl_object_of_type("Weight")
commodity_weight.Value = cWeightValue
commodity_weight.Units = "KG"
commodity.Weight = commodity_weight
munitPrice = self.shipment.create_wsdl_object_of_type("Money")
munitPrice.Amount = float(round((cCustomsValueAmnt / cQuantity), 2))
munitPrice.Currency = cCustomsValueCurrency
commodity.UnitPrice = munitPrice
self.mCommodities.append(commodity)
# ----------------------------------------------------
# add package to shipment
def set_packaging_info(self):
weight = self.invoice_info["Weight"]
type = "BOX" if weight > 0.5 else "ENVELOPE"
weight_final = float(round(weight + 0.2, 2)) if weight > 0.5 else 0.4
self.addShippingPackage(packageWeight=weight_final, physicalPackagingType=type, packagingType=f"FEDEX_{type}")
# ----------------------------------------------------
# add package to shipment
def addShippingPackage(self, packageWeight, physicalPackagingType, packagingType, packageWeightUnit="KG"):
package_weight = self.shipment.create_wsdl_object_of_type("Weight")
package_weight.Value = packageWeight
package_weight.Units = packageWeightUnit
package = self.shipment.create_wsdl_object_of_type("RequestedPackageLineItem")
package.PhysicalPackaging = physicalPackagingType
package.Weight = package_weight
self.shipment.add_package(package)
self.shipment.RequestedShipment.TotalWeight = package_weight
self.shipment.RequestedShipment.PackagingType = packagingType
# ----------------------------------------------------
# add information on duties
def setDutiesPaymentInfo(self):
mParty = self.shipment.create_wsdl_object_of_type("Party")
mParty.AccountNumber = self.CONFIG_OBJ.account_number
mParty.Address = self.shipment.RequestedShipment.Recipient.Address
mPayor = self.shipment.create_wsdl_object_of_type("Payor")
mPayor.ResponsibleParty = mParty
mPayment = self.shipment.create_wsdl_object_of_type("Payment")
mPayment.PaymentType = "RECIPIENT" # change if sender should pay duties
mPayment.Payor = mPayor
mCustomsValue = self.shipment.create_wsdl_object_of_type("Money")
mCustomsValue.Amount = self.invoice_info["Value"]
mCustomsValue.Currency = self.invoice_info["Currency"]
ccd = self.shipment.create_wsdl_object_of_type("CustomsClearanceDetail")
ccd.Commodities = self.mCommodities
ccd.CustomsValue = mCustomsValue
ccd.DutiesPayment = mPayment
self.shipment.RequestedShipment.CustomsClearanceDetail = ccd
# ----------------------------------------------------
# Set ETD (electronic trade documents) settings
def setSpecialServices(self, doc_ids):
# construct objects
ssr = self.shipment.create_wsdl_object_of_type("ShipmentSpecialServicesRequested")
ssr.SpecialServiceTypes.append("ELECTRONIC_TRADE_DOCUMENTS")
ssr.SpecialServiceTypes.append("EVENT_NOTIFICATION")
# set up ETD details
etd = self.shipment.create_wsdl_object_of_type("EtdDetail")
etd.RequestedDocumentCopies = "COMMERCIAL INVOICE"
for i, doc_id in enumerate(doc_ids, start=0):
udrd = self.shipment.create_wsdl_object_of_type("UploadDocumentReferenceDetail")
udrd.DocumentType = "COMMERCIAL_INVOICE" if i == 0 else "OTHER"
udrd.DocumentId = doc_id
udrd.Description = "Commercial_Invoice" if i == 0 else "Product_Description"
udrd.DocumentIdProducer = "CUSTOMER"
ssr.EtdDetail.DocumentReferences.append(udrd)
self.shipment.RequestedShipment.SpecialServicesRequested = ssr
# set Event Notification details
send = self.shipment.create_wsdl_object_of_type("ShipmentEventNotificationDetail")
send.AggregationType = "PER_SHIPMENT"
sens = self.shipment.create_wsdl_object_of_type("ShipmentEventNotificationSpecification")
sens.NotificationDetail.NotificationType = "EMAIL"
sens.NotificationDetail.EmailDetail.EmailAddress = self.shipment.RequestedShipment.Recipient.Contact.EMailAddress
sens.NotificationDetail.EmailDetail.Name = self.shipment.RequestedShipment.Recipient.Contact.PersonName
sens.NotificationDetail.Localization.LanguageCode = "EN"
sens.Role = "SHIPPER"
sens.Events.append("ON_SHIPMENT")
sens.Events.append("ON_EXCEPTION")
sens.Events.append("ON_DELIVERY")
sens.FormatSpecification.Type = "HTML"
send.EventNotifications = sens
self.shipment.RequestedShipment.SpecialServicesRequested.EventNotificationDetail = send
# ----------------------------------------------------
# process the shipment
def processInternationalShipment(self):
from shutil import copyfile
self.shipment.RequestedShipment.ShipTimestamp = datetime.datetime.now().replace(microsecond=0).isoformat()
# print(" ---- **** DETAILS ---- ****")
# print(self.shipment.RequestedShipment)
# print(self.shipment.ClientDetail)
# print(self.shipment.TransactionDetail)
# print("REQUESTED SHIPMENT\n\n", self.shipment.RequestedShipment)
self.shipment.send_request()
# print("RESPONSE\n\n", self.shipment.response)
status = self.shipment.response.HighestSeverity
if status == "SUCCESS" and "CompletedShipmentDetail" in self.shipment.response:
shipment_details = self.shipment.response.CompletedShipmentDetail
package_details = shipment_details.CompletedPackageDetails[0]
tracking_id = package_details.TrackingIds[0].TrackingNumber
email = self.shipment.RequestedShipment.Recipient.Contact.EMailAddress
fedex_cost = "N/A"
if hasattr(package_details, "PackageRating"):
fedex_cost = package_details.PackageRating.PackageRateDetails[0].NetCharge.Amount
# create the shipping PDF label
ascii_label_data = package_details.Label.Parts[0].Image
label_binary_data = binascii.a2b_base64(ascii_label_data)
out_path = self.invoice_info["InvoiceId"] + f"_shipment_label_{tracking_id}.pdf"
out_file = open(out_path, "wb")
out_file.write(label_binary_data)
out_file.close()
# print output information
print(
f"- SUCCESS: Created FedEx label for invoice {self.invoice_info['InvoiceId']}\n tracking ID: {tracking_id}\n email: {email}\n FedEx cost: {fedex_cost}\n Customs value: {self.invoice_info['Value']} {self.invoice_info['Currency']}\n Weight: {self.invoice_info['Weight']}\n output path: {out_path}"
)
# ----------------------------------------------------
# main script
commercial_invoice_path = "commercial_invoice_test.pdf"
product_description_1_path = "product_description_test.pdf"
sender = {
"Company": "Sender Company",
"Name": "Mr Smith",
"Address": ["Address 1", "Address 2"],
"Region": "",
"Zip": "8230",
"City": "Abyhoj",
"Country": "Denmark",
"Phone": "12345678",
"Email": "mail#mail.com",
"CountryCode": "DK",
"Currency": "EUR",
"VAT": "DK12345678",
"Residential": False,
}
recipient = {
"Company": "Recipient Co",
"Name": "Contact Name",
"Address": ["Adr1, Adr2"],
"Region": "MN",
"Zip": "55420",
"City": "Bloomington",
"Country": "United States",
"Phone": "0123456789",
"Email": "mail#mail.com",
"CountryCode": "US",
"Currency": "EUR",
"VAT": "",
"Residential": False,
}
invoice_info = {
"InvoiceId": "14385",
"Weight": 0.11,
"Quantity": 2,
"Value": 20.0,
"Shipping": 25.0,
"ShippingExpress": True,
"Currency": "EUR",
"InvoicePath": commercial_invoice_path,
"Pdfs": [product_description_1_path],
}
# print output
print(f"\n- recipient: {recipient}\n- invoice_info: {invoice_info}\n")
# create FedEx Label Helper and set configuration
flh = FedexLabelHelper()
flh.setShipmentConfig(CONFIG_OBJ=CONFIG_OBJ, invoice_info=invoice_info)
# add sender & recipient info to FedEx shipment
flh.setSenderInfo(sender)
flh.setRecipientInfo(recipient)
# set packaging based on weight
flh.set_packaging_info()
# add reference to CI as only commodity info
flh.add_ci_commodity()
# set duties payment information
flh.setDutiesPaymentInfo()
# upload documents
doc_ids = flh.upload_all_documents()
# link uploaded documents as ETD and setup event notifications
flh.setSpecialServices(doc_ids)
# process shipments and create shipping labels
flh.processInternationalShipment()

max curve time error in Heston calibration quantlib python

I am running a compiled from source SWIG python 1.16 version of QuantLib.
I have been trying to calibrate a heston model following this example.
I am only using the QL calibration at the moment to test it out before trying others.
I need time dependent parameters so I am using PiecewiseTimeDependentHestonModel.
Here is the relevant portion of my code.
Helper functions :
def tenor2date(s, base_date=None,ql=False):
# returns a date from a tenor and a base date
if base_date is None:
base_date = datetime.today()
num = float(s[:-1])
period = s[-1].upper()
if period == "Y":
return_date = base_date + relativedelta(years=num)
elif period == "M":
return_date = base_date + relativedelta(months=num)
elif period == "W":
return_date = base_date + relativedelta(weeks=num)
elif period == "D":
return_date = base_date + relativedelta(days=num)
else:
return_date = base_date
if ql:
return Date(return_date.strftime("%F"),"yyyy-mm-dd")
else:
return return_date
def setup_model(yield_ts, dividend_ts, spot, times,init_condition=(0.02, 0.2, 0.5, 0.1, 0.01)):
theta, kappa, sigma, rho, v0 = init_condition
model = ql.PiecewiseTimeDependentHestonModel(yield_ts, dividend_ts, ql.QuoteHandle(ql.SimpleQuote(spot)), v0, ql.Parameter(), ql.Parameter(),
ql.Parameter(), ql.Parameter(), ql.TimeGrid(times))
engine = ql.AnalyticPTDHestonEngine(model)
return model, engine
def setup_helpers(engine, vol_surface, ref_date, spot, yield_ts, dividend_ts):
heston_helpers = []
grid_data = []
for tenor in vol_surface:
expiry_date = tenor2date(tenor, datetime(ref_date.year(), ref_date.month(), ref_date.dayOfMonth()), True)
t = (expiry_date - ref_date)
print(f"{tenor} : {t / 365}")
p = ql.Period(t, ql.Days)
for strike, vol in zip(vol_surface[tenor]["strikes"], vol_surface[tenor]["volatilities"]):
print((strike, vol))
helper = ql.HestonModelHelper(p, calendar, spot, strike, ql.QuoteHandle(ql.SimpleQuote(vol / 100)), yield_ts, dividend_ts)
helper.setPricingEngine(engine)
heston_helpers.append(helper)
grid_data.append((expiry_date, strike))
return heston_helpers, grid_data
Market data :
vol_surface = {'12M': {'strikes': [1.0030154025220293, 0.9840808634190958, 0.9589657270688433, 0.9408279805370683, 0.9174122318462831, 0.8963792435025802, 0.8787138822765832, 0.8538712672800733, 0.8355036501980958], 'volatilities': [6.7175, 6.5, 6.24375, 6.145, 6.195, 6.425, 6.72125, 7.21, 7.5625], 'forward': 0.919323}, '1M': {'strikes': [0.9369864196692815, 0.9324482223892986, 0.9261255003380027, 0.9213195223581382, 0.9150244003650484, 0.9088253068972495, 0.9038936313900919, 0.897245676067657, 0.8924388848562849], 'volatilities': [6.3475, 6.23375, 6.1075, 6.06, 6.09, 6.215, 6.3725, 6.63125, 6.8225], 'forward': 0.915169}, '1W': {'strikes': [0.9258809998009043, 0.9236526412979602, 0.920487656155217, 0.9180490618315417, 0.9148370595017086, 0.9116231311263782, 0.9090950947170667, 0.9057357691404444, 0.9033397443834199], 'volatilities': [6.7175, 6.63375, 6.53625, 6.5025, 6.53, 6.6425, 6.77875, 6.99625, 7.1525], 'forward': 0.914875}, '2M': {'strikes': [0.9456173410343232, 0.9392447942175677, 0.9304717860942596, 0.9238709412876663, 0.9152350197527926, 0.9068086964842931, 0.9000335970840222, 0.8908167643473346, 0.884110721680849], 'volatilities': [6.1575, 6.02625, 5.8825, 5.8325, 5.87, 6.0175, 6.1975, 6.48875, 6.7025], 'forward': 0.915506}, '3M': {'strikes': [0.9533543407827232, 0.945357456067501, 0.9343646071178692, 0.9261489737826977, 0.9154251386183144, 0.9050707394248945, 0.8966770979707913, 0.8851907303568785, 0.876803402158318], 'volatilities': [6.23, 6.09125, 5.93, 5.8725, 5.915, 6.0775, 6.28, 6.60375, 6.84], 'forward': 0.915841}, '4M': {'strikes': [0.9603950279333742, 0.9509237742916833, 0.9379657828957041, 0.928295643018581, 0.9156834006905108, 0.9036539552069216, 0.8938804229269658, 0.8804999196762403, 0.870730837142799], 'volatilities': [6.3175, 6.17125, 6.005, 5.94375, 5.985, 6.15125, 6.36, 6.69375, 6.9375], 'forward': 0.916255}, '6M': {'strikes': [0.9719887962018352, 0.9599837798239937, 0.943700651576822, 0.9316544554849711, 0.9159768970939797, 0.9013018796367052, 0.8892904835162911, 0.8727031923006017, 0.8605425787295339], 'volatilities': [6.3925, 6.22875, 6.04125, 5.9725, 6.01, 6.1875, 6.41375, 6.78625, 7.0575], 'forward': 0.916851}, '9M': {'strikes': [0.9879332225745909, 0.9724112749400833, 0.951642771321364, 0.936450663789222, 0.9167103888580063, 0.8985852649047051, 0.8835274087791912, 0.8625837214139542, 0.8472311260811375], 'volatilities': [6.54, 6.34875, 6.1325, 6.055, 6.11, 6.32, 6.5875, 7.01625, 7.32], 'forward': 0.918086}}
spotDates = [ql.Date(1,7,2019), ql.Date(8,7,2019), ql.Date(1,8,2019), ql.Date(1,9,2019), ql.Date(1,10,2019), ql.Date(1,11,2019), ql.Date(1,1,2020), ql.Date(1,4,2020), ql.Date(1,7,2020)]
spotRates = [0.9148, 0.914875, 0.915169, 0.915506, 0.915841, 0.916255, 0.916851, 0.918086, 0.919323]
udl_value = 0.9148
todaysDate = ql.Date("2019-07-01","yyyy-mm-dd")
settlementDate = ql.Date("2019-07-03","yyyy-mm-dd")
and the script itself:
ql.Settings.instance().evaluationDate = todaysDate
dayCounter = ql.Actual365Fixed()
interpolation = ql.Linear()
compounding = ql.Compounded
compoundingFrequency = ql.Annual
times = [(x - spotDates[0]) / 365 for x in spotDates][1:]
discountFactors = [-log(x / spotRates[0]) / (times[i]) for i, x in enumerate(spotRates[1:])]
fwdCurve = ql.ZeroCurve(spotDates, [0] + discountFactors, dayCounter, calendar, interpolation, compounding, compoundingFrequency)
fwdCurveHandle = ql.YieldTermStructureHandle(fwdCurve)
dividendCurveHandle = ql.YieldTermStructureHandle(ql.FlatForward(settlementDate, 0, dayCounter))
hestonModel, hestonEngine = setup_model(fwdCurveHandle, dividendCurveHandle, udl_value, times)
heston_helpers, grid_data = setup_helpers(hestonEngine, vol_surface, todaysDate, udl_value, fwdCurveHandle, dividendCurveHandle)
lm = ql.LevenbergMarquardt(1e-8, 1e-8, 1e-8)
hestonModel.calibrate(heston_helpers, lm, ql.EndCriteria(500, 300, 1.0e-8, 1.0e-8, 1.0e-8))
When I run the last line I get the following error message :
RuntimeError: time (1.42466) is past max curve time (1.00274)
I do not understand how it can try to price things beyond 1Y as both the helpers and the forwards curve are defined on the same set of dates.
In case it helps someone, posting here the answer I got from the quantlb mailing :
specifying the maturity in days
t = (expiry_date - ref_date)
print(f"{tenor} : {t / 365}")
p = ql.Period(t, ql.Days)
might have an counterintuitive effect here as the specified calendar is used
to calculate the real expiry date. If the calendar is e.g. ql.UnitedStates
then this takes weekends and holidays into consideration,
ql.UnitedStates().advance(ql.Date(1,1,2019),ql.Period(365, ql.Days)) => Date(12,6,2020)
whereas
ql.NullCalendar().advance(ql.Date(1,1,2019),ql.Period(365, ql.Days)) => Date(1,1,2020)
hence I guess the interest rate curve is not long enough and throws the
error message.
So the fix is to make sure to use ql.NullCalendar() accross.

Categories

Resources