Skip to content

Commit

Permalink
Merge pull request #24 from rcholic/fix_order
Browse files Browse the repository at this point in the history
fix order placement
  • Loading branch information
rcholic authored Jun 27, 2024
2 parents d863c89 + 95d8a74 commit c19f6ad
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 42 deletions.
Binary file not shown.
7 changes: 2 additions & 5 deletions cschwabpy/SchwabAsyncClient.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,6 @@ def __auth_header(self) -> Mapping[str, str]:

async def get_account_numbers_async(self) -> List[AccountNumberWithHashID]:
await self._ensure_valid_access_token()
import json

target_url = f"{SCHWAB_TRADER_API_BASE_URL}/accounts/accountNumbers"
client = httpx.AsyncClient() if self.__client is None else self.__client
Expand Down Expand Up @@ -218,15 +217,13 @@ async def place_order_async(
await self._ensure_valid_access_token()
target_url = f"{SCHWAB_TRADER_API_BASE_URL}/accounts/{account_number_hash.hashValue}/orders"
client = httpx.AsyncClient() if self.__client is None else self.__client

try:
_header = self.__auth_header()
_header["Content-Type"] = "application/json"
print("order to place: ", json.dumps(order.to_json()))
response = await client.post(
url=target_url,
json=json.dumps(order.to_json()),
headers=self.__auth_header(),
data=json.dumps(order.to_json()),
headers=_header,
)
if response.status_code == 201:
return True
Expand Down
4 changes: 1 addition & 3 deletions cschwabpy/SchwabClient.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,6 @@ def __auth_header(self) -> Mapping[str, str]:

def get_account_numbers(self) -> List[AccountNumberWithHashID]:
self._ensure_valid_access_token()
import json

target_url = f"{SCHWAB_TRADER_API_BASE_URL}/accounts/accountNumbers"
client = httpx.Client() if self.__client is None else self.__client
Expand Down Expand Up @@ -224,11 +223,10 @@ def place_order(
try:
_header = self.__auth_header()
_header["Content-Type"] = "application/json"
print("order to place: ", json.dumps(order.to_json()))
response = client.post(
url=target_url,
json=json.dumps(order.to_json()),
headers=self.__auth_header(),
headers=_header,
)
if response.status_code == 201:
return True
Expand Down
41 changes: 39 additions & 2 deletions cschwabpy/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,45 @@
class JSONSerializableBaseModel(BaseModel):
model_config = ConfigDict(use_enum_values=True, populate_by_name=True)

def to_json(self) -> Mapping[str, Any]:
return self.model_dump(by_alias=True)
def to_json(self, drop_null_value: bool = True) -> Mapping[str, Any]:
"""Converts the object to a JSON dictionary with options to drop null values from dictionary."""
super_json = self.model_dump(by_alias=True)
if not drop_null_value:
return super_json

result: MutableMapping[str, Any] = {}
for k, v in list(super_json.items()):
if v is not None:
result[k] = self.__handle_item(v)

return result

def __handle_item(self, item: Any) -> Any:
"""Handle item in the dictionary, list or primitive values."""
if isinstance(item, dict):
return self.__del_none(item)
elif isinstance(item, List):
result = []
for itm in list(item):
result.append(self.__handle_item(itm))
return result
elif isinstance(item, set):
result = set()
for itm in set(item):
result.add(self.__handle_item(itm))
return result
else:
return item

def __del_none(self, d: MutableMapping[str, Any]) -> MutableMapping[str, Any]:
"""Recursively remove None values from a dictionary."""
for key, value in list(d.items()):
if value is None:
del d[key]
elif isinstance(value, MutableMapping):
d[key] = self.__del_none(value)

return d


class ErrorMessage(JSONSerializableBaseModel):
Expand Down
60 changes: 30 additions & 30 deletions cschwabpy/models/trade_models.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from cschwabpy.models import JSONSerializableBaseModel, OptionContractType
from typing import Optional, List, Any
from typing import Optional, List, Any, Mapping, MutableMapping
from pydantic import Field
from enum import Enum

Expand Down Expand Up @@ -289,11 +289,11 @@ class MarginInitialBalance(MarginBalance):

class AccountInstrument(JSONSerializableBaseModel):
assetType: Optional[AssetType] = None
cusip: Optional[str] = ""
description: Optional[str] = ""
instrumentId: Optional[int] = 0
cusip: Optional[str] = None
description: Optional[str] = None
instrumentId: Optional[int] = None
symbol: Optional[str] = None
netChange: Optional[float] = 0
netChange: Optional[float] = None


class AccountEquity(AccountInstrument):
Expand Down Expand Up @@ -331,12 +331,12 @@ class OrderLeg(JSONSerializableBaseModel):

class OrderLegCollection(JSONSerializableBaseModel):
orderLegType: Optional[AssetType] = None
legId: Optional[int] = 0
legId: Optional[int] = None
instrument: Optional[AccountInstrument] = None # e.g. AccountOption
instruction: Optional[OrderLegInstruction] = None
positionEffect: Optional[PositionEffect] = None
quantity: Optional[float] = None
quantityType: Optional[QuantityType] = QuantityType.SHARES
quantityType: Optional[QuantityType] = None # QuantityType.SHARES
# toSymbol: Optional[str] = None #TODO


Expand Down Expand Up @@ -366,7 +366,7 @@ class Position(JSONSerializableBaseModel):
class Account(JSONSerializableBaseModel):
type_: Optional[AccountType] = Field(None, alias="type")
accountNumber: str
roundTrips: Optional[int] = 0
roundTrips: Optional[int] = None
isDayTrader: Optional[bool] = False
isClosingOnlyRestricted: Optional[bool] = False
pfcbFlag: Optional[bool] = False
Expand Down Expand Up @@ -415,38 +415,38 @@ class OrderActivity(JSONSerializableBaseModel):


class Order(JSONSerializableBaseModel):
session: Optional[Session] = None
duration: Optional[Duration] = None
orderType: Optional[OrderType] = OrderType.LIMIT
session: Session
duration: Duration = Duration.DAY
orderType: OrderType = OrderType.LIMIT
cancelTime: Optional[str] = None
complexOrderStrategyType: Optional[ComplexOrderStrategyType] = None
quantity: Optional[float] = 0
filledQuantity: Optional[float] = 0
remainingQuantity: Optional[float] = 0
requestedDestination: Optional[Destination] = Destination.AUTO
quantity: Optional[float] = None
filledQuantity: Optional[float] = None
remainingQuantity: Optional[float] = None
requestedDestination: Optional[Destination] = None
destinationLinkName: Optional[str] = "AUTO"
releaseTime: Optional[str] = None
stopPrice: Optional[float] = None
stopPriceLinkBasis: Optional[PriceLinkBasis] = PriceLinkBasis.AVERAGE
stopPriceLinkType: Optional[PriceLinkType] = PriceLinkType.VALUE
stopPriceOffset: Optional[float] = 0
stopType: Optional[StopType] = StopType.MARK
priceLinkBasis: Optional[PriceLinkBasis] = PriceLinkBasis.AVERAGE
priceLinkType: Optional[PriceLinkType] = PriceLinkType.VALUE
price: Optional[float] = None
taxLotMethod: Optional[TaxLotMethod] = TaxLotMethod.FIFO
stopPriceLinkBasis: Optional[PriceLinkBasis] = None
stopPriceLinkType: Optional[PriceLinkType] = None
stopPriceOffset: Optional[float] = None
stopType: Optional[StopType] = None
priceLinkBasis: Optional[PriceLinkBasis] = None
priceLinkType: Optional[PriceLinkType] = None
price: float
taxLotMethod: Optional[TaxLotMethod] = None
orderLegCollection: List[OrderLegCollection] = []
activationPrice: Optional[float] = 0
specialInstruction: Optional[SpecialInstruction] = SpecialInstruction.ALL_OR_NONE
activationPrice: Optional[float] = None
specialInstruction: Optional[SpecialInstruction] = None
orderStrategyType: Optional[OrderStrategyType] = OrderStrategyType.SINGLE
orderId: Optional[int] = 0
cancelable: Optional[bool] = False
editable: Optional[bool] = False
orderId: Optional[int] = None
cancelable: Optional[bool] = None
editable: Optional[bool] = None
status: Optional[OrderStatus] = None
enteredTime: Optional[str] = None
closeTime: Optional[str] = None
tag: Optional[str] = ""
accountNumber: Optional[int] = 0 # or str ?
tag: Optional[str] = None
accountNumber: Optional[int] = None
orderActivityCollection: List[OrderActivity] = []
replacingOrderCollection: List[str] = []
childOrderStrategies: List[str] = []
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "cschwabpy"
version = "0.1.0"
version = "0.1.1"
description = ""
authors = ["Tony Wang <[email protected]>"]
readme = "README.md"
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

setup(
name="CSchwabPy",
version="0.1.0",
version="0.1.1",
description="Charles Schwab Stock & Option Trade API Client for Python.",
long_description=long_description,
long_description_content_type="text/markdown",
Expand Down

0 comments on commit c19f6ad

Please sign in to comment.