margin-estimator 0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- margin_estimator/__init__.py +11 -0
- margin_estimator/margin.py +214 -0
- margin_estimator/models.py +41 -0
- margin_estimator/py.typed +0 -0
- margin_estimator-0.1.dist-info/METADATA +151 -0
- margin_estimator-0.1.dist-info/RECORD +8 -0
- margin_estimator-0.1.dist-info/WHEEL +4 -0
- margin_estimator-0.1.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
from datetime import date, timedelta
|
|
2
|
+
from decimal import Decimal
|
|
3
|
+
|
|
4
|
+
from .models import (
|
|
5
|
+
ETFType,
|
|
6
|
+
MarginRequirements,
|
|
7
|
+
Option,
|
|
8
|
+
OptionType,
|
|
9
|
+
Underlying,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
ZERO = Decimal(0)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def calculate_margin(legs: list[Option], underlying: Underlying) -> MarginRequirements:
|
|
16
|
+
"""
|
|
17
|
+
Calculate margin for an arbitrary order according to CBOE's Margin Manual.
|
|
18
|
+
"""
|
|
19
|
+
if len(legs) == 1:
|
|
20
|
+
if legs[0].quantity > 0:
|
|
21
|
+
return _calculate_margin_long_option(legs[0])
|
|
22
|
+
return _calculate_margin_short_option(legs[0], underlying)
|
|
23
|
+
if len(legs) == 2 and legs[0].quantity < 0 and legs[1].quantity < 0:
|
|
24
|
+
return _calculate_margin_short_strangle(legs, underlying)
|
|
25
|
+
if len(legs) == 2 and legs[0].expiration != legs[1].expiration:
|
|
26
|
+
short = legs[0] if legs[0].quantity < 0 else legs[1]
|
|
27
|
+
long = legs[1] if legs[1] != short else legs[0]
|
|
28
|
+
if short.expiration > long.expiration:
|
|
29
|
+
margin = _calculate_margin_short_option(short, underlying)
|
|
30
|
+
return margin + _calculate_margin_long_option(long)
|
|
31
|
+
calls = [leg for leg in legs if leg.type == OptionType.CALL]
|
|
32
|
+
puts = [leg for leg in legs if leg.type == OptionType.PUT]
|
|
33
|
+
extra_puts = sum(c.quantity for c in calls)
|
|
34
|
+
extra_calls = sum(p.quantity for p in puts)
|
|
35
|
+
if extra_puts or extra_calls:
|
|
36
|
+
raise Exception(
|
|
37
|
+
"Ratio spreads/complex orders not supported! Try splitting your order into "
|
|
38
|
+
"smaller components and summing the results."
|
|
39
|
+
)
|
|
40
|
+
return _calculate_margin_spread(legs)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _calculate_margin_long_option(option: Option) -> MarginRequirements:
|
|
44
|
+
"""
|
|
45
|
+
Calculate margin for a single long option.
|
|
46
|
+
Source: CBOE Margin Manual
|
|
47
|
+
"""
|
|
48
|
+
if option.expiration < date.today() + timedelta(days=90):
|
|
49
|
+
return MarginRequirements(
|
|
50
|
+
# Pay for each put or call in full.
|
|
51
|
+
cash_requirement=option.price * 100 * option.quantity,
|
|
52
|
+
# Pay for each put or call in full.
|
|
53
|
+
margin_requirement=option.price * 100 * option.quantity,
|
|
54
|
+
)
|
|
55
|
+
reduced_requirement = round(option.price * 3 / 4, 2)
|
|
56
|
+
return MarginRequirements(
|
|
57
|
+
# Pay for each put or call in full.
|
|
58
|
+
cash_requirement=option.price * 100 * option.quantity,
|
|
59
|
+
# Listed: 75% of the total cost of the option.
|
|
60
|
+
margin_requirement=reduced_requirement * 100 * option.quantity,
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _calculate_margin_short_option(
|
|
65
|
+
option: Option, underlying: Underlying
|
|
66
|
+
) -> MarginRequirements:
|
|
67
|
+
"""
|
|
68
|
+
Calculate margin for a single short option.
|
|
69
|
+
Source: CBOE Margin Manual
|
|
70
|
+
"""
|
|
71
|
+
if option.type == OptionType.PUT:
|
|
72
|
+
otm_distance = max(ZERO, underlying.price - option.strike)
|
|
73
|
+
else:
|
|
74
|
+
otm_distance = max(ZERO, option.strike - underlying.price)
|
|
75
|
+
# broad-based ETFs/indices
|
|
76
|
+
if underlying.etf_type == ETFType.BROAD:
|
|
77
|
+
if option.type == OptionType.PUT:
|
|
78
|
+
minimum = round(
|
|
79
|
+
option.price + option.strike / 10 * underlying.leverage_factor, 2
|
|
80
|
+
)
|
|
81
|
+
base = round(
|
|
82
|
+
option.price
|
|
83
|
+
+ underlying.price * 3 / 20 * underlying.leverage_factor
|
|
84
|
+
- otm_distance,
|
|
85
|
+
2,
|
|
86
|
+
)
|
|
87
|
+
# 100% of option proceeds plus 15% of underlying index value less
|
|
88
|
+
# out-of-the money amount, if any, to a minimum for puts of option
|
|
89
|
+
# proceeds plus 10% of the put’s exercise price.
|
|
90
|
+
margin_requirement = max(minimum, base)
|
|
91
|
+
# Deposit cash or cash equivalents equal to aggregate exercise price
|
|
92
|
+
cash_requirement = (option.strike - option.price) * 100
|
|
93
|
+
else: # OptionType.CALL
|
|
94
|
+
minimum = round(
|
|
95
|
+
option.price + underlying.price / 10 * underlying.leverage_factor, 2
|
|
96
|
+
)
|
|
97
|
+
base = round(
|
|
98
|
+
option.price
|
|
99
|
+
+ underlying.price * 3 / 20 * underlying.leverage_factor
|
|
100
|
+
- otm_distance,
|
|
101
|
+
2,
|
|
102
|
+
)
|
|
103
|
+
# 100% of option proceeds plus 15% of underlying index value less
|
|
104
|
+
# out-of-the money amount, if any, to a minimum for calls of option
|
|
105
|
+
# proceeds plus 10% of the underlying index value.
|
|
106
|
+
margin_requirement = max(minimum, base)
|
|
107
|
+
# Deposit cash or cash equivalents equal to aggregate exercise price
|
|
108
|
+
cash_requirement = (option.strike - option.price) * 100
|
|
109
|
+
# narrow-based ETFs/indices, volatility indices, equities
|
|
110
|
+
else:
|
|
111
|
+
if option.type == OptionType.PUT:
|
|
112
|
+
minimum = round(
|
|
113
|
+
option.price + option.strike / 10 * underlying.leverage_factor, 2
|
|
114
|
+
)
|
|
115
|
+
base = round(
|
|
116
|
+
option.price
|
|
117
|
+
+ underlying.price / 5 * underlying.leverage_factor
|
|
118
|
+
- otm_distance,
|
|
119
|
+
2,
|
|
120
|
+
)
|
|
121
|
+
# 100% of option proceeds plus 20% of underlying security / index value
|
|
122
|
+
# less out-of-the-money amount, if any, to a minimum for puts of option
|
|
123
|
+
# proceeds plus 10% of the put’s exercise price.
|
|
124
|
+
margin_requirement = max(minimum, base)
|
|
125
|
+
# Deposit cash or cash equivalents equal to aggregate exercise price.
|
|
126
|
+
cash_requirement = (option.strike - option.price) * 100
|
|
127
|
+
else: # OptionType.CALL
|
|
128
|
+
minimum = round(
|
|
129
|
+
option.price + underlying.price / 10 * underlying.leverage_factor, 2
|
|
130
|
+
)
|
|
131
|
+
base = round(
|
|
132
|
+
option.price
|
|
133
|
+
+ underlying.price / 5 * underlying.leverage_factor
|
|
134
|
+
- otm_distance,
|
|
135
|
+
2,
|
|
136
|
+
)
|
|
137
|
+
# 100% of option proceeds plus 20% of underlying security / index value
|
|
138
|
+
# less out-of-the-money amount, if any, to a minimum for puts of option
|
|
139
|
+
# proceeds plus 10% of the underlying security/index value.
|
|
140
|
+
margin_requirement = max(minimum, base)
|
|
141
|
+
# Deposit underlying security.
|
|
142
|
+
cash_requirement = (underlying.price - option.price) * 100
|
|
143
|
+
margin_requirement *= 100 * abs(option.quantity)
|
|
144
|
+
return MarginRequirements(
|
|
145
|
+
cash_requirement=cash_requirement,
|
|
146
|
+
margin_requirement=margin_requirement,
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _calculate_margin_short_strangle(
|
|
151
|
+
legs: list[Option], underlying: Underlying
|
|
152
|
+
) -> MarginRequirements:
|
|
153
|
+
"""
|
|
154
|
+
Calculate margin for a short strangle.
|
|
155
|
+
Source: CBOE Margin Manual
|
|
156
|
+
"""
|
|
157
|
+
# Deposit an escrow agreement for each option.
|
|
158
|
+
margin1 = _calculate_margin_short_option(legs[0], underlying)
|
|
159
|
+
margin2 = _calculate_margin_short_option(legs[1], underlying)
|
|
160
|
+
# For the same underlying security, short put or short call requirement whichever
|
|
161
|
+
# is greater, plus the option proceeds of the other side.
|
|
162
|
+
if margin1.margin_requirement > margin2.margin_requirement:
|
|
163
|
+
margin_requirement = margin1.margin_requirement + legs[1].price * 100
|
|
164
|
+
else:
|
|
165
|
+
margin_requirement = margin2.margin_requirement + legs[0].price * 100
|
|
166
|
+
margin_requirement *= abs(legs[0].quantity)
|
|
167
|
+
return MarginRequirements(
|
|
168
|
+
cash_requirement=margin1.cash_requirement + margin2.cash_requirement,
|
|
169
|
+
margin_requirement=margin_requirement,
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def _calculate_loss_for(leg: Option, price: Decimal) -> Decimal:
|
|
174
|
+
"""
|
|
175
|
+
Calculate value at expiration for option at given price.
|
|
176
|
+
"""
|
|
177
|
+
if leg.type == OptionType.CALL:
|
|
178
|
+
itm_distance = max(ZERO, price - leg.strike)
|
|
179
|
+
else:
|
|
180
|
+
itm_distance = max(ZERO, leg.strike - price)
|
|
181
|
+
return itm_distance * leg.quantity * 100
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def _get_net_credit_or_debit(legs: list[Option]) -> Decimal:
|
|
185
|
+
"""
|
|
186
|
+
Calculate total debit/credit paid/collected for the order.
|
|
187
|
+
"""
|
|
188
|
+
total = ZERO
|
|
189
|
+
for leg in legs:
|
|
190
|
+
total += leg.quantity * leg.price * 100
|
|
191
|
+
return total
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def _calculate_margin_spread(legs: list[Option]) -> MarginRequirements:
|
|
195
|
+
"""
|
|
196
|
+
Calculate margin for a credit spread.
|
|
197
|
+
Source: CBOE Margin Manual
|
|
198
|
+
"""
|
|
199
|
+
strikes = set(leg.strike for leg in legs)
|
|
200
|
+
pnl = _get_net_credit_or_debit(legs)
|
|
201
|
+
losses = []
|
|
202
|
+
for strike in strikes:
|
|
203
|
+
points = [_calculate_loss_for(leg, strike) for leg in legs]
|
|
204
|
+
losses.append(sum(points)) # type: ignore
|
|
205
|
+
margin_requirement = abs(min(losses)) + pnl
|
|
206
|
+
|
|
207
|
+
return MarginRequirements(
|
|
208
|
+
# deposit and maintain cash or cash equivalents equal to the spread’s maximum
|
|
209
|
+
# potential loss
|
|
210
|
+
cash_requirement=margin_requirement,
|
|
211
|
+
# the lesser of the amount required for the short option(s), or the spread’s
|
|
212
|
+
# maximum potential loss
|
|
213
|
+
margin_requirement=margin_requirement,
|
|
214
|
+
)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from datetime import date
|
|
2
|
+
from decimal import Decimal
|
|
3
|
+
from enum import StrEnum
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ETFType(StrEnum):
|
|
9
|
+
BROAD = "broad-based"
|
|
10
|
+
NARROW = "narrow-based"
|
|
11
|
+
VOLATILITY = "volatility"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class OptionType(StrEnum):
|
|
15
|
+
CALL = "C"
|
|
16
|
+
PUT = "P"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Option(BaseModel):
|
|
20
|
+
expiration: date
|
|
21
|
+
price: Decimal
|
|
22
|
+
quantity: int
|
|
23
|
+
strike: Decimal
|
|
24
|
+
type: OptionType
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class MarginRequirements(BaseModel):
|
|
28
|
+
cash_requirement: Decimal
|
|
29
|
+
margin_requirement: Decimal
|
|
30
|
+
|
|
31
|
+
def __add__(self, other: "MarginRequirements"):
|
|
32
|
+
return MarginRequirements(
|
|
33
|
+
cash_requirement=self.cash_requirement + other.cash_requirement,
|
|
34
|
+
margin_requirement=self.margin_requirement + other.margin_requirement,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class Underlying(BaseModel):
|
|
39
|
+
etf_type: ETFType | None = None
|
|
40
|
+
leverage_factor: Decimal = Decimal(1)
|
|
41
|
+
price: Decimal
|
|
File without changes
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: margin-estimator
|
|
3
|
+
Version: 0.1
|
|
4
|
+
Summary: Calculate estimated margin requirements for equities, options, futures, and futures options. Based on CBOE and CME margining.
|
|
5
|
+
Project-URL: Homepage, https://github.com/tastyware/margin-estimator
|
|
6
|
+
Author-email: Graeme Holliday <graeme.holliday@pm.me>
|
|
7
|
+
License: MIT License
|
|
8
|
+
|
|
9
|
+
Copyright (c) 2024 tastyware
|
|
10
|
+
|
|
11
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
12
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
13
|
+
in the Software without restriction, including without limitation the rights
|
|
14
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
15
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
16
|
+
furnished to do so, subject to the following conditions:
|
|
17
|
+
|
|
18
|
+
The above copyright notice and this permission notice shall be included in all
|
|
19
|
+
copies or substantial portions of the Software.
|
|
20
|
+
|
|
21
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
22
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
23
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
24
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
25
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
26
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
27
|
+
SOFTWARE.
|
|
28
|
+
License-File: LICENSE
|
|
29
|
+
Requires-Python: >=3.11
|
|
30
|
+
Requires-Dist: pydantic>=2.9.2
|
|
31
|
+
Description-Content-Type: text/markdown
|
|
32
|
+
|
|
33
|
+
# margin-estimator
|
|
34
|
+
Calculate estimated margin requirements for equities, options, futures, and futures options. Based on CBOE and CME margining.
|
|
35
|
+
|
|
36
|
+
> [!NOTE]
|
|
37
|
+
> Not all features are available yet, pending further development.
|
|
38
|
+
> Currently, equity/ETF/index options are supported, for any trade
|
|
39
|
+
> type other than ratio spreads, box spreads, and jaze lizards.
|
|
40
|
+
> Contributions welcome!
|
|
41
|
+
|
|
42
|
+
## Installation
|
|
43
|
+
|
|
44
|
+
```console
|
|
45
|
+
$ pip install margin_estimator
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Usage
|
|
49
|
+
|
|
50
|
+
Simply pass a list of legs to the `calculate_margin` function along with an `Underlying` object containing information on the underlying, and you'll get back margin requirement estimates for cash and margin accounts!
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
from datetime import date
|
|
54
|
+
from decimal import Decimal
|
|
55
|
+
from margin_estimator import (
|
|
56
|
+
ETFType,
|
|
57
|
+
Option,
|
|
58
|
+
OptionType,
|
|
59
|
+
Underlying,
|
|
60
|
+
calculate_margin,
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
# a SPY iron condor
|
|
64
|
+
# make sure to pass `ETFType.BROAD` for broad-based indices
|
|
65
|
+
underlying = Underlying(price=Decimal("587.88"), etf_type=ETFType.BROAD)
|
|
66
|
+
expiration = date(2024, 12, 20)
|
|
67
|
+
long_put = Option(
|
|
68
|
+
expiration=expiration,
|
|
69
|
+
price=Decimal("4.78"),
|
|
70
|
+
quantity=1,
|
|
71
|
+
strike=Decimal(567),
|
|
72
|
+
type=OptionType.PUT,
|
|
73
|
+
)
|
|
74
|
+
short_put = Option(
|
|
75
|
+
expiration=expiration,
|
|
76
|
+
price=Decimal("5.61"),
|
|
77
|
+
quantity=-1,
|
|
78
|
+
strike=Decimal(572),
|
|
79
|
+
type=OptionType.PUT,
|
|
80
|
+
)
|
|
81
|
+
short_call = Option(
|
|
82
|
+
expiration=expiration,
|
|
83
|
+
price=Decimal("5.23"),
|
|
84
|
+
quantity=-1,
|
|
85
|
+
strike=Decimal(602),
|
|
86
|
+
type=OptionType.CALL,
|
|
87
|
+
)
|
|
88
|
+
long_call = Option(
|
|
89
|
+
expiration=expiration,
|
|
90
|
+
price=Decimal("3.68"),
|
|
91
|
+
quantity=1,
|
|
92
|
+
strike=Decimal(607),
|
|
93
|
+
type=OptionType.CALL,
|
|
94
|
+
)
|
|
95
|
+
margin = calculate_margin(
|
|
96
|
+
[long_put, short_put, long_call, short_call], underlying
|
|
97
|
+
)
|
|
98
|
+
print(margin)
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
>>> cash_requirement=Decimal('262.00') margin_requirement=Decimal('262.00')
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
For normal equities you can omit the `etf_type` parameter:
|
|
106
|
+
|
|
107
|
+
```python
|
|
108
|
+
# a short F put
|
|
109
|
+
underlying = Underlying(price=Decimal("11.03"))
|
|
110
|
+
expiration = date(2024, 12, 20)
|
|
111
|
+
put = Option(
|
|
112
|
+
expiration=expiration,
|
|
113
|
+
price=Decimal("0.45"),
|
|
114
|
+
quantity=-1,
|
|
115
|
+
strike=Decimal(11),
|
|
116
|
+
type=OptionType.PUT,
|
|
117
|
+
)
|
|
118
|
+
margin = calculate_margin([put], underlying)
|
|
119
|
+
print(margin)
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
```python
|
|
123
|
+
>>> cash_requirement=Decimal('1055.00') margin_requirement=Decimal('263.00')
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
And for leveraged products, you'll need to pass in the `leverage_factor`:
|
|
127
|
+
|
|
128
|
+
```python
|
|
129
|
+
# a naked TQQQ call
|
|
130
|
+
underlying = Underlying(
|
|
131
|
+
price=Decimal("77.35"),
|
|
132
|
+
etf_type=ETFType.BROAD,
|
|
133
|
+
leverage_factor=Decimal(3),
|
|
134
|
+
)
|
|
135
|
+
expiration = date(2024, 12, 20)
|
|
136
|
+
call = Option(
|
|
137
|
+
expiration=expiration,
|
|
138
|
+
price=Decimal("4.45"),
|
|
139
|
+
quantity=-1,
|
|
140
|
+
strike=Decimal(80),
|
|
141
|
+
type=OptionType.CALL,
|
|
142
|
+
)
|
|
143
|
+
margin = calculate_margin([call], underlying)
|
|
144
|
+
print(margin)
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
```python
|
|
148
|
+
>>> cash_requirement=Decimal('7555.00') margin_requirement=Decimal('3661.00')
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Please note that all numbers are baseline estimates based on CBOE/CME guidelines and individual broker margins will likely vary significantly.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
margin_estimator/__init__.py,sha256=jicOQATyGlqpsGfXsCIoVouspMVHO1HRShIsn3xCzDI,202
|
|
2
|
+
margin_estimator/margin.py,sha256=3RSxHFpWQNPHcD9Mj6YBDeY0dXdYqeWnhuG6KUqH8Gw,8573
|
|
3
|
+
margin_estimator/models.py,sha256=lMjUyL1ufrjXOSgkUUBL3c5bZwOKn3AsFOmGZkZVfF8,892
|
|
4
|
+
margin_estimator/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
+
margin_estimator-0.1.dist-info/METADATA,sha256=fK7E5YVtF0RMfV1ap64WX31ikrQt8r52-mRE3OQGKCM,4624
|
|
6
|
+
margin_estimator-0.1.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
|
|
7
|
+
margin_estimator-0.1.dist-info/licenses/LICENSE,sha256=DxJk7vwH5bPnqpXaMQ06BWBM6BVfBOx8GzFmqpKm8oQ,1066
|
|
8
|
+
margin_estimator-0.1.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 tastyware
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|